题意:一棵树,每个节点是黑色或白色,你可以从任意节点开始进行一些操作并在任意节点结束,如果当前在$x$,那么一次操作可以是:1.走到相邻节点$y$并翻转$y$的颜色,2.翻转$x$的颜色,问把所有节点都变黑最少要多少次操作

首先当然要把黑色叶子全部删掉,这个类似拓扑排序一样做即可

然后证一个小结论:不会有一条边被经过$\gt2$次

设$(x,y)$被经过$3$次,那么操作序列为$A\Rightarrow(x\rightarrow y)\Rightarrow B\Rightarrow(y\rightarrow x)\Rightarrow C\Rightarrow(x\rightarrow y)\Rightarrow D$,我们可以把操作序列变为$A\Rightarrow C\Rightarrow\text~x\Rightarrow(x\rightarrow y)\Rightarrow\text~y\Rightarrow B\Rightarrow D$,这样等价并且没有增加操作次数

所以最优解形如:选定起点$s$和终点$t$,$s\rightarrow t$路径上的边只经过一次,其他边经过两次,并在过程中适时使用操作$2$

更进一步:存在最优解使得$s,t$都是叶子

假设起点为$x$,终点为$y$且$y$不是叶子,$z$是$y$往远离$x$方向的第一个节点,那么把原来的$(y\rightarrow z)\Rightarrow X\Rightarrow(z\rightarrow y)$变成$\text~y\Rightarrow(y\rightarrow z)\Rightarrow X$即可,同样不增加操作次数

现在考虑怎么求答案,如果$s=t$,设$d_x$为$x$的度数并记$v_x=\left[(d_x\equiv1(\bmod2),c_x=B)\text{ or }(d_x\equiv0(\bmod2),c_x=W)\right]$,那么答案是$2|E|+\sum\limits_xv_x$

当$t$移动时,$s\rightarrow t$上的边经过次数$-1$,$s\rightarrow t$这条链上$\neq t$的点(以下记这条链为$[s,t)$)的贡献也要重新计算,要加上$-\text{dis}(s,t)+\sum\limits_{x\in[s,t)}-[v_x=1]+[v_x=0]$

所以如果我们给每个点$x$一个权值$-[v_x=1]+[v_x=0]-1$,找到最小的叶子到叶子的链即可,dfs一遍统计答案即可

monochrome:单色的,黑白的

#include<stdio.h>
#include<algorithm>
using namespace std;
int h[100010],nex[200010],to[200010],M,n;
void add(int a,int b){
	M++;
	to[M]=b;
	nex[M]=h[a];
	h[a]=M;
}
int d[100010],q[100010];
bool del[100010];
char s[100010];
void topsort(){
	int head,tail,x,i;
	head=1;
	tail=0;
	for(i=1;i<=n;i++){
		if(s[i]=='B'&&d[i]==1)q[++tail]=i;
	}
	while(head<=tail){
		x=q[head++];
		del[x]=1;
		for(i=h[x];i;i=nex[i]){
			if(d[to[i]])d[to[i]]--;
			if(s[to[i]]=='B'&&d[to[i]]==1&&!del[to[i]])q[++tail]=to[i];
		}
	}
}
int f[100010],sum,mn;
int tp(int x){return(d[x]&1)^(s[x]=='W');}
int val(int x){return tp(x)==1?-2:0;}
void dfs(int fa,int x){
	sum+=(tp(x)==1)+2;
	f[x]=val(x);
	for(int i=h[x];i;i=nex[i]){
		if(to[i]!=fa&&!del[to[i]]){
			dfs(x,to[i]);
			mn=min(mn,f[x]+f[to[i]]);
			f[x]=min(f[x],f[to[i]]+val(x));
		}
	}
}
int main(){
	int i,x,y;
	scanf("%d",&n);
	for(i=1;i<n;i++){
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
		d[x]++;
		d[y]++;
	}
	scanf("%s",s+1);
	topsort();
	for(x=1;x<=n;x++){
		if(!del[x])break;
	}
	if(x>n)
		putchar('0');
	else{
		dfs(0,x);
		sum-=2;
		printf("%d",sum+mn);
	}
}

最新文章

  1. css常用代码
  2. chomre常用快捷键
  3. Oracle时间戳 与时间之间的相互转换
  4. SAP研究贴之--发票校验提示移动平均价为负
  5. android FakeWindow的小应用大用途
  6. 1.js编程风格。 --- 编写可维护的javascript
  7. 【分享】LateX排版软件学习教程合集
  8. mysql获取当前时间,前一天,后一天
  9. java中队列Queue的使用
  10. SOC(网络安全管理平台)
  11. gulp打开gbk编码的html文件乱码
  12. docker配置代理的用户名密码
  13. s21day17 python笔记
  14. wordclock中文模式快一个小时怎么调整
  15. HTML中引用外部JS文件失效原因
  16. 滑动拼图 Sliding Puzzle
  17. gradlew 的https代理设定
  18. Maven构建应用程序常用配置(转)
  19. How to Pronounce SAY, SAYS, and SAID
  20. [置顶] 利用Global.asax的Application_Error实现错误记录,错误日志

热门文章

  1. [bzoj1005][HNOI2008]明明的烦恼-Prufer编码+高精度
  2. rhel-server srpms iso
  3. 【Educationcal Codeforces Round 21】
  4. 重写Alert
  5. python manage.py 命令
  6. 判断ArcSDE是否安装成功
  7. Redis -- 数据类型小结
  8. MiCode108 猜数字
  9. Hive分组取第一条记录
  10. Windows + IDEA 手动开发MapReduce程序