「JOISC 2020 Day4」首都城市
题目大意:给定一棵树,每个点有颜色
求选择一个最小的颜色集合,使得这些颜色的点能够构成一个连通块
容易发现,选取这个颜色就必须将这个颜色连通路径上的所有其它颜色选掉
但是要纠正一个:
并不是选取的这个颜色的连通路径上的颜色就行
因为选取另一个颜色,可能导致不在当前连通路径上的其它颜色也需要被选取
[
]
这样的关系构成一个有向图,一条边表示选了(u)就选(v)
因此可以考虑( ext{tarjan})强连通缩点,由于最终我们选择强连通分量一定没有出边(否则不优)
因此可以线性统计答案,问题在于如何建立这个图
首先考虑如何将这个连通路径提取出来,一种简单的办法是:找到这个颜色所有点的( ext{LCA}),路径就可以表示为( ext{LCA})到所有点路径的并
Solution1
树剖线段树完成路径连边
点数为(O(n)),边数为(O(nlog ^2n))
Solution2
倍增连边
点数边数均为(O(nlog n))
Solution3
离线,用类似( ext{tarjan LCA})的方式,维护一个并查集
每次并查集的父亲关系改变时,新建节点,即可完成一个类似可持久化的操作
如果再用( ext{tarjan})预处理( ext{LCA}),复杂度/点数/边数 就为(O(nalpha(n)))
#include<bits/stdc++.h>
using namespace std;
typedef pair <int,int> Pii;
#define pb push_back
#define mp make_pair
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
char buf[200000],*p1,*p2;
#define getchar() (((p1==p2)&&(p2=(p1=buf)+fread(buf,1,200000,stdin))),*p1++)
int rd(){
int s=0;static char c;
while(c=getchar(),c<48);
do s=(s<<1)+(s<<3)+(c^'0');
while(c=getchar(),c>47);
return s;
}
const int N=2e5+10,INF=1e9+10,K=N*3.5;
int n,k,m;
int A[N],L[N],F[N],C[N],Fir[N],I[N];
vector <int> G[N],V[N];
struct Edge{
int to,nxt;
} e[K*2];
int head[K],ecnt;
void AddEdge(int u,int v){
if(u==v) return;
e[++ecnt]=(Edge){v,head[u]};
head[u]=ecnt;
}
int Find1(int x){ return F[x]==x?x:F[x]=Find1(F[x]); }
void pre_dfs(int u,int f){
F[u]=u;
if(!Fir[A[u]]) Fir[A[u]]=u;
if(--C[A[u]]==0) L[A[u]]=Find1(Fir[A[u]]);
for(int v:G[u]) if(v!=f) pre_dfs(v,u);
F[u]=f;
}
Pii Find(int x){
if(F[x]==x) return mp(F[x],I[x]);
Pii t=Find(F[x]);
return AddEdge(++m,t.second),AddEdge(m,I[x]),F[x]=t.first,mp(F[x],I[x]=m);
}
void dfs(int u,int f){
F[u]=u,I[u]=A[u];
for(int v:G[u]) if(v!=f) dfs(v,u);
for(int v:V[u]) AddEdge(A[v],Find(v).second);
F[u]=f;
}
int t[K],low[K],ins[K],stk[K],top,dfn;
int ans=1e9,vis[N],out[K];
void dfs(int u){
t[u]=low[u]=++dfn,ins[stk[++top]=u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!t[v]) dfs(v),cmin(low[u],low[v]);
else if(ins[v]) cmin(low[u],t[v]);
}
if(low[u]==t[u]){
int fl=1,tmp=top;
for(int v=-1;v!=u;){
v=stk[top--];
for(int i=head[v];i;i=e[i].nxt) if(!ins[e[i].to]) { fl=0; break; }
}
rep(i,top+1,tmp) ins[stk[i]]=0;
if(fl) {
int res=0;
rep(i,top+1,tmp) {
int x=stk[i];
if(x<=k && !vis[x]) vis[x]=1,res++;
}
rep(i,top+1,tmp) if(stk[i]<=k) vis[stk[i]]=0;
if(res) cmin(ans,res-1);
}
}
}
int main(){
n=rd(),k=rd();
rep(i,2,n) {
int u=rd(),v=rd();
G[u].pb(v),G[v].pb(u);
}
rep(i,1,n) C[A[i]=rd()]++;
pre_dfs(1,0);
rep(i,1,n) V[L[A[i]]].pb(i);
m=k;
dfs(1,0);
rep(i,1,k) if(!t[i]) dfs(i);
printf("%d
",ans);
fprintf(stderr,"Vertices =%d
Edges =%d
",m,ecnt);
}