[ICPC2015 WF] Tours

一、题目

点此看题

二、解法

首先考虑如果所有的环不交,那么答案就是环长度 \(\gcd\) 的所有因子。想一想相交的情况复杂在哪里?我们的常规方法是按照 \(\tt dfs\) 的顺序考察返祖边构成的环,但是这些环可能会组合生成新环,进而产生更多限制:

上图所展示的三部分分别表示:两个环的交、第一个环减去交的部分、第二个环减去交的部分。那么所有组合出来的环合法的充要条件是:这些基本部分分别合法(充分性显然,必要性通过反证法可以说明)

我们维护一个集族 \(S\),初始时包含图上所有环,每次从中取出两个相交的边集,再放回操作后三个不交的部分。那么最后的答案就是所有边集大小 \(\tt gcd\) 的因子。但是我们不可能暴力进行这个过程,只能考察最后结果的性质。

关键的 \(\tt observation\) 是:两条边 \(e_1,e_2\) 最后处在同一个边集的充要条件是,初始时它们在同一个环中,并且对于初始时候的所有环,它们要不然同时包含 \(e_1,e_2\),要不然不含有 \(e_1,e_2\) 的任意一个。

那么我们可以枚举 \(e_1\),统计 \(e_2\) 的个数,就得到了这个边集的大小。那么 \(e_1,e_2\) 初始一定是非割边,并且删去 \(e_1\) 之后 \(e_2\)变成割边。因为这说明了它们初始在同一个环中,并且没有环只包含 \(e_1\) 或者 \(e_2\)

所以枚举删去哪条非割边,然后跑 \(\tt tarjan\),时间复杂度 \(O(m^2)\)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 2005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,tot=1,f[M],cut[M],zxy[M];
int Ind,cnt,res,ban,ans,dfn[M],low[M];
struct edge{int v,next;}e[M<<1];
int gcd(int a,int b) {return !b?a:gcd(b,a%b);}
void tarjan(int u,int fa)
{
	dfn[u]=low[u]=++Ind;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if((i>>1)==ban || v==fa) continue;
		if(!dfn[v])
		{
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u]) cut[i>>1]=1,res++;
		}
		else low[u]=min(low[u],dfn[v]);
	}
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		e[++tot]=edge{v,f[u]},f[u]=tot;
		e[++tot]=edge{u,f[v]},f[v]=tot;
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i,0);
	for(int i=1;i<=m;i++) zxy[i]=cut[i];
	cnt=res;
	for(int i=1;i<=m;i++) if(!zxy[i])
	{
		for(int j=1;j<=n;j++) dfn[j]=low[j]=0;
		res=0;ban=i;Ind=0;
		for(int j=1;j<=n;j++)
			if(!dfn[j]) tarjan(j,0);
		ans=gcd(ans,res-cnt+1);
	}
	for(int i=1;i<=m;i++)
		if(ans%i==0) printf("%d ",i);
	puts("");
}
posted @ 2022-06-09 16:34  C202044zxy  阅读(95)  评论(0编辑  收藏  举报