AT4133 [ARC097D] Monochrome Cat 题解

题目链接
参考了题解
先用一种类似拓扑排序的过程,把颜色为黑色的叶子删掉(注意:删点后新出现的叶子也算)
然后就只剩下颜色为白的叶子。我们必然要把它们全部经过
我们考虑一个很捞的过程(学长讲评原话),即随便选一个根,然后按照dfs的过程暴力遍历修改
把这个过程称为原过程
考虑这个过程要操作多少次
在这个过程里,每条边被经过了两遍。
但是有的点,这样修改不行,所以要在经过它们时再多修改一次。
考虑将这个很捞的过程优化
注意到这个过程是回到起点的。但是题目要求不一定要回到起点
那么我们把起点到终点的减掉就行了(即不用从终点回到起点)
因为我们要把叶子全部经过,所以这样的过程一定是最优答案的候选(即最优解一定是这样的形式)
如何把起点到终点的路径减掉?qiuzhijingjike
考虑这个路径上的点,分两种情况
1.原过程做完后不需再改变颜色,这种点可以少遍历一次,但是它需要再染一次色,这种点对答案的贡献为0
2.原过程做完后需要再改变颜色,这种点可以少遍历一次,同时还省去了一次染色,这种点对答案的贡献为-2
我们把1种点点权赋值为0,2种点点权赋值为2,树形DP求直径即可

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>

using namespace std;
#define int long long
const int N = 100005;
int ver[N<<1],nxt[N<<1],head[N<<1],val[N],tot;
int deg[N],c[N],cnt,rt;
int edge1[N],edge2[N]; 
int vis[N],n,res,d[N];
char s[N];
inline int read() {
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
void add(int x,int y) {
	ver[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}
void del() {
	queue<int> q;
	for(int i = 1;i <= n;i++) {
		if(deg[i] == 1&&c[i] == 2) {
			q.push(i);
			vis[i] = 1;
		}
	}
	while(!q.empty()) {
		int x = q.front();q.pop();
		vis[x] = 1;
		for(int i = head[x];i;i = nxt[i]) {
			int y = ver[i];
			if(vis[y]) continue;
			if(!vis[y]) deg[y]--;
			if(deg[y] == 1&&c[y] == 2) q.push(y);  
		}
	}
	return;
}
void dfs(int x,int fa) {
	d[x] = val[x];
	for(int i = head[x];i;i = nxt[i]) {
		int y = ver[i];
		if(y == fa) continue;
		dfs(y,x);
		res = max(res,d[x] + d[y]);
		d[x] = max(d[x],d[y] + val[x]);
	}
} 
signed main () {
	n = read();
	for(int i = 1;i < n;i++) {
		int u,v;
		u = read();v = read();
		add(u,v);add(v,u);
		edge1[i] = u;
		edge2[i] = v;
		++deg[u]; 
		++deg[v];
	}
	scanf("%s",s+1);
	for(int i = 1;i <= n;i++) {
		if(s[i] == 'W') c[i] = 1;
		if(s[i] == 'B') c[i] = 2;
	}
	del();
//	cout<<"fuck";
	memset(deg,0,sizeof(deg));
	memset(head,0,sizeof(head));
	tot = 0;
	for(int i = 1;i < n;i++) {
		if(vis[edge1[i]]||vis[edge2[i]]) continue;
		add(edge1[i],edge2[i]);
		add(edge2[i],edge1[i]);
		deg[edge2[i]]++;
		deg[edge1[i]]++;
	}
//	cout<<"fuck";
	for(int i = 1;i <= n;i++) {
		if(vis[i]) continue;
		if(((deg[i]&1)&&c[i] == 2)||((deg[i]%2==0)&&(c[i] == 1))) {
			val[i] = 2;
			++cnt;
		}
		else val[i] = 0;
	}
	for(int i = 1;i <= n;i++) {
		if(!vis[i]) {
			rt = i;
			break;
		}
	}
	dfs(rt,0);
	printf("%lld\n",tot + cnt - res);
	return 0; 
} 
posted @ 2020-12-29 17:33  ctt2006  阅读(99)  评论(0)    收藏  举报