「JOISC 2020 Day4」首都城市

「JOISC 2020 Day4」首都城市

题目大意:给定一棵树,每个点有颜色

求选择一个最小的颜色集合,使得这些颜色的点能够构成一个连通块


容易发现,选取这个颜色就必须将这个颜色连通路径上的所有其它颜色选掉

但是要纠正一个:

并不是选取的这个颜色的连通路径上的颜色就行

因为选取另一个颜色,可能导致不在当前连通路径上的其它颜色也需要被选取

\[\ \]

这样的关系构成一个有向图,一条边表示选了\(u\)就选\(v\)

因此可以考虑\(\text{tarjan}\)强连通缩点,由于最终我们选择强连通分量一定没有出边(否则不优)

因此可以线性统计答案,问题在于如何建立这个图

首先考虑如何将这个连通路径提取出来,一种简单的办法是:找到这个颜色所有点的\(\text{LCA}\),路径就可以表示为\(\text{LCA}\)到所有点路径的并

Solution1

树剖线段树完成路径连边

点数为\(O(n)\),边数为\(O(n\log ^2n)\)

Solution2

倍增连边

点数边数均为\(O(n\log n)\)

Solution3

离线,用类似\(\text{tarjan LCA}\)的方式,维护一个并查集

每次并查集的父亲关系改变时,新建节点,即可完成一个类似可持久化的操作

如果再用\(\text{tarjan}\)预处理\(\text{LCA}\),复杂度/点数/边数 就为\(O(n\alpha(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\n",ans);
	fprintf(stderr,"Vertices =%d \nEdges =%d\n",m,ecnt);
}
posted @ 2021-03-17 14:18  chasedeath  阅读(78)  评论(0编辑  收藏  举报