P6914 - Tours 题解

我们称若干个不相交的简单环的并叫做复合环,也就是每个点为偶度的边集。如果每个简单环都满足条件,那么所有复合环显然也都满足条件。考虑两个简单环 \(A,B\),那么 \(A\oplus B\) 显然必定是复合环,它也要满足条件。我们将 \(A\cup B\) 拆成三个不相交的部分:\(S_1=A\cap B,S_2=A-A\cap B,S_3=B-A\cap B\),那么 \(A,B,A\oplus B\) 都由其中恰好两个组成。直觉告诉我们 \(S_1,S_2,S_3\) 这三个边集都要满足条件。显然它们满足条件是 \(A,B,A\oplus B\) 的充分条件,但是必不必要呢?反证,不妨设 \(S_1\) 中某个颜色数量偏少,那么 \(S_2,S_3\) 的该颜色都要偏多,那么 \(S_2\cup 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;
}
posted @ 2021-08-12 22:44  ycx060617  阅读(85)  评论(0编辑  收藏  举报