zoukankan      html  css  js  c++  java
  • P6914

    我们称若干个不相交的简单环的并叫做复合环,也就是每个点为偶度的边集。如果每个简单环都满足条件,那么所有复合环显然也都满足条件。考虑两个简单环 (A,B),那么 (Aoplus B) 显然必定是复合环,它也要满足条件。我们将 (Acup B) 拆成三个不相交的部分:(S_1=Acap B,S_2=A-Acap B,S_3=B-Acap B),那么 (A,B,Aoplus B) 都由其中恰好两个组成。直觉告诉我们 (S_1,S_2,S_3) 这三个边集都要满足条件。显然它们满足条件是 (A,B,Aoplus B) 的充分条件,但是必不必要呢?反证,不妨设 (S_1) 中某个颜色数量偏少,那么 (S_2,S_3) 的该颜色都要偏多,那么 (S_2cup S_3) 该颜色必定偏多,不行,必要性得证。

    于是简单环 (A,B) 就可以等价地拆成 (S_1,S_2,S_3)​ 这三个不相交的边集来完成条件。然后还可以对其它的边集对(此时不只有简单环了)进行这样拆,直到场上剩下的所有边集都互不相交,就是每条边恰好属于一个类。

    为什么要拆成不相交的边集们呢。因为这样有独立性,舒服啊。答案显然是最终每个类的大小的 gcd(不属于任何简单环的边类除外,很好理解吧)。

    两条边在同一个类中的充要条件显然是它们所在简单环集合相等。这玩意好像还有个学名叫切边等价。考虑把所有这样的切边等价类求出来,好像还挺可做的,毕竟虽然简单环个数是指数级的,但是我们只需要判断两条边所在简单环集合是否相等,不需要真的求出来简单环集合。

    数据范围给的是平方的,正好够我们对每对边判断它们切边等价。怎么判断?边所在简单环,想到边双理论。一条边不在任何简单环上当且仅当它是割边。而如果不是割边,那么把它割掉,虽然图还连通,但是它所在的所有简单环都消失。如果此时我们再跑 tarjan 判另一条边是否割边,如果是割边,说明它所在的简单环,割掉的边都在。这说明一个的所在简单环集合包含于另一个。而如果反过来判一下,不就可以确定是否相等了吗,不就完事了吗!复杂度平方,跑 (m)​ 遍 tarjan,对每对边记录包含性,最后再 dfs 或者并查集划分出切边等价类。

    code
    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    const int N=2010,M=2010;
    int n,m;
    int a[M],b[M],t[N][N];
    vector<int> nei[N];
    bool to[N][N];
    int dfn[N],low[N],nowdfn;
    bool cut[M];
    int ban;
    void tar(int x,int fa=0){
    	dfn[x]=low[x]=++nowdfn;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];
    		if(x==a[ban]&&y==b[ban]||x==b[ban]&&y==a[ban])continue;
    		if(y==fa){fa=-1;continue;}
    		if(!dfn[y]){
    			tar(y,x),low[x]=min(low[x],low[y]);
    			if(dfn[x]<low[y])cut[t[x][y]]=true;
    		}
    		else low[x]=min(low[x],dfn[y]);
    	}
    }
    struct ufset{
    	int fa[M];
    	ufset(){memset(fa,0,sizeof(fa));}
    	int root(int x){return fa[x]?fa[x]=root(fa[x]):x;}
    	void mrg(int x,int y){
    		x=root(x),y=root(y);
    		if(x!=y)fa[x]=y;
    	}
    }ufs;
    int gcd(int x,int y){return y?gcd(y,x%y):x;}
    int cnt[M];
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		a[i]=x,b[i]=y;
    		nei[x].pb(y),nei[y].pb(x);
    		t[x][y]=t[y][x]=i;
    	}
    	for(int i=1;i<=m;i++){
    		ban=i;
    		memset(dfn,0,sizeof(dfn)),nowdfn=0;
    		memset(cut,0,sizeof(cut));
    		for(int j=1;j<=n;j++)if(!dfn[j])tar(j);
    		for(int j=1;j<=m;j++)if(cut[j])to[i][j]=true;
    	}
    	for(int i=1;i<=m;i++)for(int j=1;j<=m;j++)if(to[i][j]&&to[j][i])ufs.mrg(i,j);
    	ban=0;
    	memset(dfn,0,sizeof(dfn)),nowdfn=0;
    	memset(cut,0,sizeof(cut));
    	for(int i=1;i<=n;i++)if(!dfn[i])tar(i);
    	for(int i=1;i<=m;i++)cnt[ufs.root(i)]++;
    	int ans=0;
    	for(int i=1;i<=m;i++)if(!cut[i])ans=gcd(ans,cnt[i]);
    	for(int i=1;i<=m;i++)if(ans%i==0)cout<<i<<" ";
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    work two year[转]
    知名技术博客内容聚合网站
    VS2010注册码
    某公司的一个题面试题(wfcfan)
    asp.net控件开发基础系列
    .NET (C#) Internals: Delegates1
    可空类型细微见真知!
    C#中操作XML Node节点细节操作
    sql server数据库性能的优化
    字符串精确匹配算法改进的探讨
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/solution-p6914.html
Copyright © 2011-2022 走看看