YbtOJ 714「点分治」染色计划

题目链接:YbtOJ #714

小 A 有一棵 $n$ 个点的无根树,其中编号为 $i$ 的节点初始颜色为 $c_i$。

一次染色操作可以将某种颜色的点 全部 染成另一种颜色。即可以选择两种颜色 $C1,C2$,令当前所有等于 $C1$ 的 $c_i$ 变成 $C2$。

求至少执行多少次染色操作,使得存在一种颜色 $C$,满足对于任意一对 $c_x=c_y=C$ 的点 $x,y$,树上 $x,y$ 路径中的节点的颜色都是 $C$。

$1\le n\le2\times10^5$,$1\le k\le n$,$1\le c_i\le k$。

Solution

点分治。

强制所选的连通块必含分治中心。

从与分治中心同色的所有点开始 BFS,每次将队首的父节点同色的所有点加入队列。

若在过程中出现了当前分治连通块之外的点,则这样得到的答案肯定不会比已有答案更优,直接结束 BFS。否则,就可以在 BFS 结束后更新答案。

这样一来每次 BFS 范围都在分治连通块内,复杂度正确。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,fma")
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define W while
#define I inline
#define RI register int
#define LL long long
#define Cn const
#define CI Cn int&
#define gc getchar
#define D isdigit(c=gc())
#define pc(c) putchar((c))
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
namespace Debug{
Tp I void _debug(Cn char* f,Ty t){cerr<<f<<'='<<t<<endl;}
Ts I void _debug(Cn char* f,Ty x,Ar... y){W(*f!=',') cerr<<*f++;cerr<<'='<<x<<",";_debug(f+1,y...);}
Tp ostream& operator<<(ostream& os,Cn vector<Ty>& V){os<<"[";for(Cn auto& vv:V) os<<vv<<",";os<<"]";return os;}
#define gdb(...) _debug(#__VA_ARGS__,__VA_ARGS__)
}using namespace Debug;
namespace FastIO{
Tp I void read(Ty& x){char c;int f=1;x=0;W(!D) f=c^'-'?1:-1;W(x=(x<<3)+(x<<1)+(c&15),D);x*=f;}
Ts I void read(Ty& x,Ar&... y){read(x),read(y...);}
Tp I void write(Ty x){x<0&&(pc('-'),x=-x,0),x<10?(pc(x+'0'),0):(write(x/10),pc(x%10+'0'),0);}
Tp I void writeln(Cn Ty& x){write(x),pc('\n');}
}using namespace FastIO;
Cn int N=2e5+10,inf=2e9;
int n,m,F[N],c[N],Ans=inf,used[N],Min,cnt,id,sz[N],vis[N];
#define PA pair<int,int>
#define MP make_pair
#define fi first
#define se second
vector<int> G[N],cg[N],vc[N],v;
#define pb push_back
I void GetG(CI x,CI fa){
RI mx=0;sz[x]=1;for(auto i:G[x]) i^fa&&!vis[i]&&(GetG(i,x),sz[x]+=sz[i],mx=max(mx,sz[i]));
mx=max(mx,cnt-sz[x]),mx<Min&&(Min=mx,id=x);
}
I void Get(CI x,CI fa){F[x]=fa,v.pb(x);for(auto i:G[x]) i^fa&&!vis[i]&&(Get(i,x),0);}
I void Cle(CI x,CI fa){F[x]=0;for(auto i:G[x]) i^fa&&!vis[i]&&(Cle(i,x),0);}
queue<int> q;
I void Dfs(CI x){
RI i,j,X=0;vis[x]=1;v.clear(),Get(x,0);for(auto i:v) vc[c[i]].pb(i),used[c[i]]=0;
W(!q.empty()) q.pop();used[c[x]]=1,q.push(c[x]);W(!q.empty()){
RI u=q.front();q.pop(),X++;if(cg[u].size()!=vc[u].size()){X=inf;break ;}
for(auto i:vc[u]) F[i]&&(!used[c[F[i]]]&&(q.push(c[F[i]]),used[c[F[i]]]=1));
}Ans=min(Ans,X);for(auto i:v) vc[c[i]].clear();Cle(x,0);
for(auto i:G[x]) !vis[i]&&(cnt=sz[i],Min=inf,GetG(i,0),Dfs(id),0);
}
int main(){
freopen("color.in","r",stdin),freopen("color.out","w",stdout);
RI i,x,y;for(read(n,m),i=1;i<n;i++) read(x,y),G[x].pb(y),G[y].pb(x);
for(i=1;i<=n;i++) read(c[i]),cg[c[i]].pb(i);Min=inf,cnt=n,GetG(1,0),Dfs(id);
return writeln(Ans-1),0;
}