[题解]CF1442E Black, White and Grey Tree
应该是一个没人写的做法。
思路
显然对于一个仅由白点或仅由黑点构成的连通块均可以被一次操作给消除,考虑将所有仅有一种颜色的连通块先缩成一个点。
接着发掘一下灰点的性质,发现若一个灰点连接了多个黑色连通块,那么这些黑色连通块都可以在一次操作中全部消除掉。于是我们将这些由同一个灰点连接的所有同色连通块全部缩成一个点。
注意到灰点对操作次数毫无影响,因此此时直接删除灰点即可,那么现在我们得到了一个节点颜色黑白相间的一棵树。
令此时这棵树的直径为 ,则容易证明答案为 。考虑如下证明:
根据直径的定义,直径一定是树上最长的一条链,因此我们消除直径上的点的过程中一定可以将直径上的点挂着的点全部删掉。此时只需计算删除直径所需的操作次数是多少即可。(下文中的点数均为仅保留直径的点数)
- 若 ,则直径两端点的颜色相同,每次删除直径的端点即可,操作次数为 。接下来证明不可能有答案比 小,考虑上述构造答案的方式,若有另一种构造答案的方式则必定是在一次操作中删掉了三个点,那么必定会将直径拆成了两个连通块,并且这两个连通块大小均为奇数,则最终必定会剩下单独的一个节点次数将会多消耗一次操作,但是若我们不选择拆成两个连通块则不会多这一步。由此得证!
- 若 ,则直径两段颜色不同,我们先随便删掉任意一个直径端点,然后直径长度变成了一个偶数,由此得到答案为 。其余证明与 类似,不再细说。
Code
#include <bits/stdc++.h>
#define re register
#define fst first
#define snd second
#define chmax(a,b) (a = max(a,b))
using namespace std;
typedef pair<int,int> pii;
const int N = 2e5 + 10,M = N * 2;
int n; bool vis[N];
int rt,arr[N],f[N],dep[N];
vector<int> g[N],v[N],E[N];
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
inline int find(int x){
if (f[x] == x) return f[x];
else return f[x] = find(f[x]);
}
inline void merge(int a,int b){
int x = find(a),y = find(b);
if (x != y) f[x] = y;
}
inline void dfs(int u,int fa){
dep[u] = dep[fa] + (bool)(arr[u]);
if (dep[rt] < dep[u]) rt = u;
for (int v:g[u]){
if (v != fa) dfs(v,u);
}
}
inline void solve(){
n = read(); rt = 0;
fill(dep + 1,dep + n + 1,0);
fill(vis + 1,vis + n + 1,false);
for (re int i = 1;i <= n;i++){
g[i].clear(); v[i].clear(); E[i].clear();
}
for (re int i = 1;i <= n;i++) arr[f[i] = i] = read();
for (re int i = 1,a,b;i < n;i++){
a = read(),b = read();
E[a].push_back(b);
E[b].push_back(a);
if (arr[a] == arr[b]) merge(a,b);
}
for (re int i = 1;i <= n;i++) v[find(i)].push_back(i);
for (re int i = 1;i <= n;i++){
if (!arr[i] && !vis[i]){
vector<int> a,b;
for (int u:v[find(i)]){
vis[u] = true;
for (int v:E[u]){
if (arr[v] == 1) a.push_back(v);
else if (arr[v] == 2) b.push_back(v);
}
}
for (re int i = 1;i < a.size();i++) merge(a[i - 1],a[i]);
for (re int i = 1;i < b.size();i++) merge(b[i - 1],b[i]);
}
} fill(vis + 1,vis + n + 1,false);
for (re int i = 1;i <= n;i++){
if (!arr[i] && !vis[i]){
vector<int> a,b;
for (int u:v[find(i)]){
vis[u] = true;
for (int v:E[u]){
if (arr[v] == 1) a.push_back(v);
else if (arr[v] == 2) b.push_back(v);
}
}
if (!a.empty()){
int x = find(i),y = find(a.front());
g[x].push_back(y); g[y].push_back(x);
}
if (!b.empty()){
int x = find(i),y = find(b.front());
g[x].push_back(y); g[y].push_back(x);
}
}
}
for (re int u = 1;u <= n;u++){
if (!arr[u]) continue;
for (int v:E[u]){
if (!arr[v]) continue;
int x = find(u),y = find(v);
if (x != y) g[x].push_back(y);
}
}
for (re int i = 1;i <= n;i++){
if (!g[i].empty()){
dfs(i,0); dfs(rt,0);
break;
}
}
int Max = 0;
for (re int i = 1;i <= n;i++) chmax(Max,dep[i]);
printf("%d\n",Max / 2 + 1);
}
int main(){
int T; T = read();
while (T--) solve();
return 0;
}
作者:WaterSun
出处:https://www.cnblogs.com/WaterSun/p/18733014
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】