zoukankan      html  css  js  c++  java
  • 「JOISC 2020 Day4」首都城市

    「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);
    }
    
  • 相关阅读:
    记录软件工程课程项目开发时遇到的各种小问题(django)
    用python实现逻辑回归
    利用KD树进行异常检测
    PyQt4入门学习笔记(五)
    PyQt4入门学习笔记(四)
    import matplolib 时出现"This probably means that tk wasn't installed properly."的解决方法
    IDEA上安装和使用checkstyle,findbugs,visualVM,PMD插件
    pycharm连接mysql数据库
    基于trie树做一个ac自动机
    用python实现最长公共子序列算法(找到所有最长公共子串)
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14549112.html
Copyright © 2011-2022 走看看