zoukankan      html  css  js  c++  java
  • IOI2021集训队作业277BK Tours

    给出一个无向图,你需要给每条边染(k)种色,使得:对于每个环,这(k)种色的出现次数相同。

    问有哪些合法的(k)

    (n,mle 2000)


    假如环不相交,那么可以将每个环上的边分成一个集合。把集合大小求(gcd)即可。

    当两个环相交的时候,发现原本的两个集合(S_1,S_2)要分成三类:(S_1igcap S_2,S_1setminus S_1igcap S_2,S_2setminus S_1igcap S_2)

    推广到更多的环,可以发现:两条边在同一个集合,当且仅当包含它们的环的集合相同。

    问题变为求出所有的集合(注意忽略桥边)。

    到了这里我卡住了。%%%ymq秒掉。

    判断两条边(a,b)是否在同一集合(称其为等价类)。先删(a)看看(b)是否在环中,再删(b)看看(a)是否在环中。如果都不在,那么它们为等价类。时间(O(m^2))

    还有更妙的做法:建dfs树,对于非树边随机赋一个权值,将其跨过的树边的权值都异或上这个权值。最终权值相同的为等价类。

    简要说明正确性:首先两条非树边不可能为等价类,因为非树边可以和树边组成环;其次不需要考虑两条或以上非树边所组成的环,因为这个环一定可以拆成多个只有一条非树边的环,并且每个环覆盖的树边为这个环覆盖的树边的子集。

    时间(O(m+mlg m))(后面这个(lg)map


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 2005
    int gcd(int a,int b){
    	int k;
    	while (b)
    		k=a%b,a=b,b=k;
    	return a;
    }
    int n,m;
    struct EDGE{
    	int to;
    	EDGE *las;
    } e[N*2];
    int ne;
    EDGE *last[N];
    void link(int u,int v){
    	e[ne]={v,last[u]};
    	last[u]=e+ne++;
    }
    int era;
    int bz[N][N];
    int dfn[N],low[N],cnt;
    void dfs(int x,int fa,int bz[]){
    	low[x]=dfn[x]=++cnt;
    	for (EDGE *ei=last[x];ei;ei=ei->las){
    		if (ei-e>>1==era || ei->to==fa)
    			continue;
    		if (!dfn[ei->to]){
    			dfs(ei->to,x,bz);
    			low[x]=min(low[x],low[ei->to]);
    			if (low[ei->to]<=dfn[x])
    				bz[ei-e>>1]=1;
    		}
    		else{
    			low[x]=min(low[x],dfn[ei->to]);
    			bz[ei-e>>1]=1;
    		}
    	}
    }
    int dsu[N],c[N];
    int getdsu(int x){return dsu[x]==x?x:dsu[x]=getdsu(dsu[x]);}
    void work(int x){
    	era=x;
    	memset(dfn,0,sizeof(int)*(n+1));
    	cnt=0;
    	for (int i=1;i<=n;++i)
    		if (!dfn[i])
    			dfs(i,0,bz[x]);
    }
    int main(){
    	freopen("in.txt","r",stdin);
    	freopen("right.txt","w",stdout);
    	scanf("%d%d",&n,&m);
    	for (int i=0;i<m;++i){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		link(u,v),link(v,u);
    	}
    	work(m);
    	for (int i=0;i<m;++i)
    		if (bz[m][i])
    			work(i);
    //	for (int i=0;i<=m;++i,printf("
    "))
    //		for (int j=0;j<m;++j)
    //			printf("%d ",bz[i][j]);
    	for (int i=0;i<m;++i)
    		dsu[i]=i;
    	for (int i=0;i<m;++i)
    		if (bz[m][i])
    			for (int j=0;j<m;++j)
    				if (bz[m][j] && bz[i][j]==0 && bz[j][i]==0)
    					dsu[getdsu(i)]=getdsu(j);
    	for (int i=0;i<m;++i)
    		if (bz[m][i])
    			c[getdsu(i)]++;
    	int g=0;
    	for (int i=0;i<m;++i)
    		if (bz[m][i])
    			g=gcd(g,c[i]);
    	printf("1");
    	for (int i=2;i<=m;++i)
    		if (g%i==0)
    			printf(" %d",i);
    	return 0;
    }
    
    
    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    #include <ctime>
    #include <map>
    #define N 2005
    #define ll long long
    #define irand (rand()*RAND_MAX+rand())
    #define lrand ((ll)irand*RAND_MAX*RAND_MAX+irand)
    int gcd(int a,int b){
    	int k;
    	while (b)
    		k=a%b,a=b,b=k;
    	return a;
    }
    int n,m;
    struct EDGE{
    	int to;
    	EDGE *las;
    } e[N*2];
    int ne;
    EDGE *last[N];
    void link(int u,int v){
    	e[ne]={v,last[u]};
    	last[u]=e+ne++;
    }
    ll v[N],s[N];
    map<ll,int> ma;
    int vis[N],ins[N];
    void dfs(int x,int fa=0){
    	vis[x]=1;
    	ins[x]=1;
    	for (EDGE *ei=last[x];ei;ei=ei->las){
    		if (ei->to==fa) continue;
    		if (!vis[ei->to]){
    			dfs(ei->to,x);
    			s[x]^=v[ei-e>>1]=s[ei->to];
    		}
    		else if (ins[ei->to]){
    			s[ei->to]^=v[ei-e>>1]=lrand;
    			s[x]^=v[ei-e>>1];
    		}
    	}
    	ins[x]=0;
    }
    int main(){
    	freopen("in.txt","r",stdin);
    	freopen("out.txt","w",stdout);
    	srand(time(0));
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=m;++i){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		link(u,v),link(v,u);
    	}
    	for (int i=1;i<=n;++i)
    		if (!vis[i])
    			dfs(i);
    	for (int i=0;i<m;++i)
    		if (v[i])
    			ma[v[i]]++;
    	int g=0;
    	for (auto p=ma.begin();p!=ma.end();++p)
    		g=gcd(g,p->second);
    	printf("1");
    	for (int i=2;i<=m;++i)
    		if (g%i==0)
    			printf(" %d",i);
    	return 0;
    }
    
    
  • 相关阅读:
    PAT 甲级 1132 Cut Integer (20 分)
    AcWing 7.混合背包问题
    AcWing 9. 分组背包问题
    AcWing 5. 多重背包问题 II
    AcWing 3. 完全背包问题
    AcWing 4. 多重背包问题
    AcWing 2. 01背包问题
    AcWing 875. 快速幂
    AcWing 874. 筛法求欧拉函数
    AcWing 873. 欧拉函数
  • 原文地址:https://www.cnblogs.com/jz-597/p/14042426.html
Copyright © 2011-2022 走看看