AGC034E

发布时间 2023-11-09 09:43:44作者: yinhee

虽然做法大致相同,但是本篇题解将会讲述如何想出正解,分享我的思路。望通过。

首先看到题目,容易想到一个简单很多的情况:在一条链上,且终点确定。此时就可以把终点两边的点分开,分别计算到终点的距离之和,看是否相等即可。

没有确定终点时,枚举一个终点即可。

考虑将这种做法带入本题。先 \(O(n)\) 枚举终点,然后会发现,这其实是一个判定问题:如果可以全部到终点,那么答案就是 \(\dfrac{\sum_{c_i=1}dep_i}{2}\)。这一点下面会证明。\(c_i\) 表示点 \(i\) 上是否有棋子。

此时发现没什么突破口,于是开始找性质。我们称一个棋子 \(x\)\(y\) 向他们中心,向上移动为 \(x\)\(y\) 拉起来。

Lemma 1:用一个 \(x\) 的祖先拉起它是没有意义的。

Proof:首先,进行这样的操作之后 \(dep\) 之和是不变的,所以不会使答案更小。那么使这个操作有意义,就只有一种可能:它让 \(x\) 点被拉起来的机会增加了。

经过思考可以发现,\(x\) 被拉上去是不会使机会增加的,因为这只可能会增加和他互为祖先后代关系的点的数量。而没有这种的点对很明显是更好地将两个点都拉起来的。

Lemma 2:如果只考虑以 \(x\) 为根的子树内的点能不能被拉到根,优先找子树内的两个点操作是更优的。

Proof:如果先找一个子树外的点和一个子树内的点操作,可能子树内原有 \((x,y)\) 是有意义的操作,操作后就不是了。

再回到题目,由于 Lemma 2,我们是从一个子树再向外操作,很容易想到树形 dp。问题就在于怎样定义状态。

考虑记录经过若干次操作,子树内棋子到根 \(dep\) 之和。但是此时 \(dep\) 之和最小不一定是最优的,因为我们可能需要用这些点去拉起来子树外的点,所以考虑记录一个范围 \([f_u,g_u]\)。容易发现,这个范围内奇偶性与端点相同的值都是可以取到的。

\(g_u\) 的转移是 naive 的,因为写太久了所以不作讲解。对于 \(f_u\) 的转移,我们一定是想办法尽量拉上来现在最深的点,其他就好办了。则我们要让最深的点尽可能靠近根。设 \(siz_u\)\(u\) 子树内棋子数,\(mx\)\(\max f_v+siz_v\)。那其他点也尽可能深,但是不超过最深的,这样更有可能拉上来最深的点。则记 \(s=\sum\min(g_v+siz_v,mx)\),如果 \(s\ge 2\times mx\),那么显然是可以把所有点拉到根的,但是 \(g_u\bmod 2=1\) 时例外,\(\sum dep_i\) 最小为 \(1\),否则 \(f_u=2\times mx-s\)。判断 \(f_{rt}\) 是否为 \(0\) 即可。

总复杂度 \(O(n^2)\)

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();
}