题目
一(n)个节点的树,节点被分成(k)个集合,(i)属于(S_i),
一条边是可划分的当且仅当左右两边的子树不存在相同集合的点
你一次可以合并两个集合,求最少的操作次数使得所有边都不可划分
$N le 5 imes 10^5 , S_i le K le N $
题解
-
如果(S_x=S_y),那么$ x (到) y $路径上的边都不可划分,把他们缩起来
-
即把所有相同颜色的点两两路径的连通块缩起来
-
得到一个所有节点颜色不同的树
-
相当于连最少的边使得对应路径覆盖所有树边
-
答案是(叶子数+1)/2
#include<bits/stdc++.h> using namespace std; const int N=500010; int n,m,f[N],c[N],fa[N],dep[N],o=1,hd[N],d[N]; struct Edge{int v,nt;}E[N<<1]; char gc(){ static char*p1,*p2,s[1000000]; if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin); return(p1==p2)?EOF:*p1++; } int rd(){ int x=0;char C=gc(); while(C<'0'||C>'9')C=gc(); while(C>='0'&&C<='9')x=(x<<1)+(x<<3)+C-'0',C=gc(); return x; } void adde(int u,int v){ E[o]=(Edge){v,hd[u]};hd[u]=o++; E[o]=(Edge){u,hd[v]};hd[v]=o++; } int find(int x){return f[x]==x?x:f[x]=find(f[x]);} void dfs(int u){ f[u]=u; for(int i=hd[u];i;i=E[i].nt){ int v=E[i].v; if(v==fa[u])continue; fa[v]=u;dep[v]=dep[u]+1; dfs(v); } } void merge(int u,int v){ u=find(u),v=find(v); while(u!=v){ if(dep[u]<dep[v])swap(u,v); f[u]=find(fa[u]),u=f[u]; } } int main(){ freopen("mergers.in","r",stdin); freopen("mergers.out","w",stdout); n=rd();m=rd(); for(int i=1;i<n;++i)adde(rd(),rd()); dfs(1); for(int i=1,x;i<=n;++i){ x=rd(); if(!c[x])c[x]=i; else merge(c[x],i); } for(int i=1;i<o;i+=2){ int fu=find(E[i].v),fv=find(E[i+1].v); if(fu!=fv)d[fu]++,d[fv]++; } int lf=0; for(int i=1;i<=n;++i)if(d[i]==1)lf++; cout<<((lf+1)>>1)<<endl; return 0; }