首先看到题目,容易想到一个简单很多的情况:在一条链上,且终点确定。此时就可以把终点两边的点分开,分别计算到终点的距离之和,看是否相等即可。
没有确定终点时,枚举一个终点即可。
考虑将这种做法带入本题。先 枚举终点,然后会发现,这其实是一个判定问题:如果可以全部到终点,那么答案就是 。这一点下面会证明。 表示点 上是否有棋子。
此时发现没什么突破口,于是开始找性质。我们称一个棋子 和 向他们中心,向上移动为 被 拉起来。
Lemma 1:用一个 的祖先拉起它是没有意义的。
Proof:首先,进行这样的操作之后 之和是不变的,所以不会使答案更小。那么使这个操作有意义,就只有一种可能:它让 点被拉起来的机会增加了。
经过思考可以发现, 被拉上去是不会使机会增加的,因为这只可能会增加和他互为祖先后代关系的点的数量。而没有这种的点对很明显是更好地将两个点都拉起来的。
Lemma 2:如果只考虑以 为根的子树内的点能不能被拉到根,优先找子树内的两个点操作是更优的。
Proof:如果先找一个子树外的点和一个子树内的点操作,可能子树内原有 是有意义的操作,操作后就不是了。
再回到题目,由于 Lemma 2,我们是从一个子树再向外操作,很容易想到树形 dp。问题就在于怎样定义状态。
考虑记录经过若干次操作,子树内棋子到根 之和。但是此时 之和最小不一定是最优的,因为我们可能需要用这些点去拉起来子树外的点,所以考虑记录一个范围 。容易发现,这个范围内奇偶性与端点相同的值都是可以取到的。
的转移是 naive 的,因为写太久了所以不作讲解。对于 的转移,我们一定是想办法尽量拉上来现在最深的点,其他就好办了。则我们要让最深的点尽可能靠近根。设 为 子树内棋子数, 为 。那其他点也尽可能深,但是不超过最深的,这样更有可能拉上来最深的点。则记 ,如果 ,那么显然是可以把所有点拉到根的,但是 时例外, 最小为 ,否则 。判断 是否为 即可。
总复杂度 。
code:
int n,m,c[N],siz[N];
ll f[N],g[N];
char s[N];
int tot,head[N];
struct node{int to,nxt;}e[N<<1];
il void add(int u,int v){e[++tot]={v,head[u]},head[u]=tot;}
void dfs(int u,int fa){
siz[u]=c[u];
ll s=0,mx=0;
go(i,u){
int v=e[i].to;
if(v==fa)continue;
dfs(v,u);
mx=max(mx,f[v]+siz[v]);
g[u]+=g[v]+siz[v];
siz[u]+=siz[v];
}
go(i,u){
int v=e[i].to;
if(v==fa)continue;
s+=min(g[v]+siz[v],mx);
}
if(s>=2*mx)f[u]=g[u]&1;
else f[u]=2*mx-s;
}
void Yorushika(){
scanf("%d%s",&n,s+1);
rep(i,1,n-1){
int u,v;scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
rep(i,1,n)c[i]=s[i]-'0';
ll ans=1ll*inf*inf;
rep(i,1,n){
mems(f,0),mems(g,0);now=i;
dfs(i,0);
if(!f[i])ans=min(ans,g[i]/2);
}
printf("%lld\n",ans>1e15?-1:ans);
}
signed main(){
int t=1;
// scanf("%d",&t);
while(t--)
Yorushika();
}