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

浙公网安备 33010602011771号