[??记录]AT4133 [ARC097D] Monochrome Cat

command_block

2021-01-12 18:42:47

Personal

**题意** : 给出一棵 $n$ 个点的树,初始时每个点为黑色或白色。 以任意节点开始操作,每轮操作可以选择是否移动到相邻节点,然后将当前所在的节点颜色取反。 求使所有点变为黑色的最小操作次数。 $n\leq 10^5$ ,时限$\texttt{2s}$。 ------------ 完全不会啊qwq 首先,显然只需要保留全体白色点的虚树。 然后,必须要将这颗树完整遍历一次,但经过每个点的次数随着起止点的选取而变化,不便分析。 不妨先考虑回路,这样,每条边都会被正反各走一次,每个点都恰会被经过度数次。 这样走过之后,标记树上剩余的白点,只需要在此处停留一步就能将其变为黑点。 回路的问题已经解决了,现在考虑起止点的选择。假设选择 $u\rightarrow v$ ,那么可以让 $[u,v)$ 中所有点少经过一次。 - 对于没有标记的点,少经过一次,所以需要停留一回合,没有贡献。 - 对于有标记的点,少经过一次,所以不需要再停留,贡献为 $-2$。 所以,选择一条标记点最多的路径即可。 注意,$v$ 是不包含在路径内的,所以需要特判只有一个白点的情况。 只要有多个白点,叶节点处的白点肯定不是标记点,选出的路径必然可以以两个无标记叶节点为端点。这样,端点在不在路径内就无关紧要了。 最终答案为 $2\times$ 虚树边数 $+$ 标记点数 $-2\times$ 路径最大标记点数。 ```cpp #include<algorithm> #include<cstdio> #include<vector> #define pb push_back #define MaxN 105000 using namespace std; vector<int> g[MaxN]; char col[MaxN];int s[MaxN],ans; void pfs(int u,int fa) { s[u]=col[u]; for (int i=0,v;i<g[u].size();i++) if ((v=g[u][i])!=fa){ pfs(v,u); s[u]+=s[v]; if (s[v]>0)ans+=2; } } int f[MaxN],len; void dfs(int u,int fa) { len=max(len,(int)col[u]); for (int i=0,v;i<g[u].size();i++) if ((v=g[u][i])!=fa){ dfs(v,u); len=max(len,f[u]+f[v]+col[u]); f[u]=max(f[u],f[v]); } f[u]+=col[u]; } int n; int main() { scanf("%d",&n); for (int i=1,u,v;i<n;i++){ scanf("%d%d",&u,&v); g[u].pb(v);g[v].pb(u); }scanf("%s",col+1); int rt=0; for (int i=1;i<=n;i++) if (col[i]=(col[i]=='W')) rt=i; if (!rt){printf("0");return 0;} pfs(rt,0); if (s[rt]==1){printf("1");return 0;} for (int u=1;u<=n;u++)if (s[u]){ int cnt=0; for (int i=0;i<g[u].size();i++) cnt+=(s[g[u][i]]>0); ans+=(col[u]=(cnt&1)^col[u]); } dfs(rt,0); printf("%d",ans-2*len); return 0; } ```