• [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("");
    }
    
  • 相关阅读:
    #Git 21天打卡第一天 01天0526
    老徐第六期百人计划之职业发展方向&学习方向
    LR12.53安装中文补丁包,录制后回放脚本一致卡在编译的问题
    常用oracle语句整理
    LoadRunner11之批量插入SQL数据~2
    LoadRunner12之SQLServer数据库批量插入--.Net协议
    Jmeter连接Oracle数据库简单使用
    AppScan安装使用
    SQL多表连接
    [剑指Offer] 4.二维数组的查找
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16359844.html
Copyright © 2020-2023  润新知