【NOI2008T1】假面舞会-DFS环处理+最大公因数

测试地址:假面舞会

做法:这题看上去就是找环然后求最大公因数,然后下一步就不会做了,看了题解就跪了......主要这个处理方法太神奇了。

首先建图肯定不能按照原来的图建,因为需要枚举起点来走,会TLE,所以我们对于原图a->b这条边,连一条a->b边权为1,再连一条b->a边权为-1。如果图中有非零环,那么最大的答案就是所有非零环长的最大公因数,最小答案就是所有非零环长的最小的大于2的公因数。如果图中没有非零环,就统计每个连通块的最长链长,这个长度加上1就是这个连通块的最大答案,累加每个连通块的最大答案就是整体的最大答案,最小答案就是3。如果最大答案小于3则无解。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 1000000000
using namespace std;
int n,m,cnt[100010],first[100010]={0},tot=0,maxn,minn;
int len[100010]={0},ans1,ans2;
struct edge {int v,d,next;} e[2000010];
bool vis[100010]={0},flag=0;

int gcd(int a,int b)
{
  return b!=0?gcd(b,a%b):a;
}

void insert(int a,int b,int d)
{
  e[++tot].v=b,e[tot].d=d,e[tot].next=first[a],first[a]=tot;
}

void dfs(int v)
{
  vis[v]=1;
  maxn=max(maxn,cnt[v]);
  minn=min(minn,cnt[v]);
  for(int i=first[v];i;i=e[i].next)
  {
    if (!vis[e[i].v])
	{
	  cnt[e[i].v]=cnt[v]+e[i].d;
	  dfs(e[i].v);
	}
    else if (cnt[v]+e[i].d-cnt[e[i].v]!=0)
	{
	  flag=1;
	  if (ans1==0) ans1=abs(cnt[v]+e[i].d-cnt[e[i].v]);
	  else ans1=gcd(ans1,abs(cnt[v]+e[i].d-cnt[e[i].v]));
	}
  }
}

int main()
{
  scanf("%d%d",&n,&m);
  for(int i=1,a,b;i<=m;i++)
  {
    scanf("%d%d",&a,&b);
	insert(a,b,1),insert(b,a,-1);
  }
  
  ans1=0;
  for(int i=1;i<=n;i++)
  if (!vis[i])
  {
    cnt[i]=0;
	maxn=-inf,minn=inf;
	dfs(i);
	if (!flag) ans1+=maxn-minn+1;
  }
  
  ans1=abs(ans1);
  if (ans1<3) ans1=-1,ans2=-1;
  else if (!flag) ans2=3;
  else
  {
    for(int i=3;i<=ans1;i++)
	  if (ans1%i==0) {ans2=i;break;}
  }
  
  printf("%d %d",ans1,ans2);
  
  return 0;
}


原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793752.html