测试地址:假面舞会
做法:这题看上去就是找环然后求最大公因数,然后下一步就不会做了,看了题解就跪了......主要这个处理方法太神奇了。
首先建图肯定不能按照原来的图建,因为需要枚举起点来走,会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;
}