[NOI2008 假面舞会]

[关键字]:图论 公约数 找环和链

[题目大意]:有K类面具,第i类只能看见属于第i+1类的编号的面具,现给出一些可以看见的面具的信息,问最多和最少有多少面具。

//===========================================================================================================

[分析]:一开是想到了这些信息一定可以被连成一些环和链。如果有环那链一定就没有用了,怎样求出环中最大最小的种类数呢?一开是没仔细想好题以为只能出现一个环,后来看了题解可以出现多个环,而所有环的公约数就是可行的k,所以在有环的情况下题目要求的解就是所有环的最大公约数和大于3的最小公约数。而没有环的话链的最小长度毫无疑问是3,而最大长度就是每个联通块的最长链的长度和。这里面有个技巧,因为如果有两个面具能看见同一个或一个能看见两个,那这两个的一定属于同一类,而且也有可能出现这样的联通块:1->2->3->4->5且6->7->5这样就变得不好处理了。可以把有向边换成无向边正向的话类数+1,反向的话类数-1。这样一来如果找到已经表过号的点就是找到了环,环的长度就是abs(将要编的号-已有编号)。而最长链就是一个联通块内最大编号-最小编号(因为有可能出现负数或0)。

[代码]:

View Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN=100100;
const int INF=0x7fffffff;

struct node
{
int y,d,op,next;
}e[MAXN*20];
int first[MAXN];
int n,m,tot,sum,ans1,ans2;
int circle[MAXN],num[MAXN];
int Max[MAXN],Min[MAXN],belong[MAXN];
bool ve[MAXN*20],vp[MAXN];

void Add(int x,int y)
{
e[++tot].y=y;
e[tot].d=1;
e[tot].next=first[x];
first[x]=tot;
e[tot].op=tot+1;
e[++tot].y=x;
e[tot].d=-1;
e[tot].next=first[y];
first[y]=tot;
e[tot].op=tot-1;
}

void Init()
{
scanf("%d%d",&n,&m);
//printf("%d\n",n);
for (int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
Add(x,y);
}
}

void DFS(int v,int k)
{
num[v]=k,vp[v]=1;
for (int i=first[v];i;i=e[i].next)
if (!ve[i])
{
ve[i]=ve[e[i].op]=1;
if (!vp[e[i].y]) DFS(e[i].y,k+e[i].d); else
if (k+e[i].d-num[e[i].y]!=0)
circle[++sum]=abs(k+e[i].d-num[e[i].y]);
}
}

int Gcd(int x,int y)
{
if (x<y) swap(x,y);
while (y!=0)
{
int t=x%y;
x=y;
y=t;
}
return x;
}

void DFS2(int v,int sum)
{
belong[v]=sum;
for (int i=first[v];i;i=e[i].next)
if (belong[e[i].y]==0) DFS2(e[i].y,sum);
}

void DFS3(int v,int k)
{
num[v]=k,vp[v]=1;
for (int i=first[v];i;i=e[i].next)
if (!ve[i])
{
ve[i]=ve[e[i].op]=1;
DFS3(e[i].y,k+e[i].d);
}
}

void Solve()
{
memset(ve,0,sizeof(ve));
memset(num,0,sizeof(num));
memset(vp,0,sizeof(vp));
// printf("%d\n",n);
for (int i=1;i<=n;++i)
{
if (!vp[i]) DFS(i,1);
//printf("%d\n",num[i]);
}
//for (int i=1;i<=sum;++i) printf("%d\n",circle[i]);
ans1=circle[1];int temp=circle[1];
for (int i=2;i<=sum;++i)
{
ans1=Gcd(ans1,circle[i]);
if (temp>circle[i]) temp=circle[i];
}
for (int i=3;i<=temp;++i)
{
bool flag=1;
for (int j=1;j<=sum;++j)
if (circle[j]%i!=0) {flag=0;break;}
if (flag) {ans2=i;break;}
}
//printf("%d %d\n",ans1,ans2);
if (sum!=0)
{
if (ans1>=3) printf("%d %d\n",ans1,ans2); else printf("-1 -1\n");
return;
}
memset(ve,0,sizeof(ve));
memset(vp,0,sizeof(vp));
for (int i=1;i<=n;++i)
if (belong[i]==0)
{
++sum;
DFS2(i,sum);
Max[sum]=-INF;
Min[sum]=INF;
}
memset(num,0,sizeof(num));
for (int i=1;i<=n;++i)
{
if (!vp[i]) DFS3(i,1);
if (num[i]>Max[belong[i]]) Max[belong[i]]=num[i];
if (num[i]<Min[belong[i]]) Min[belong[i]]=num[i];
}
ans1=0;
for (int i=1;i<=sum;++i)
ans1+=Max[i]-Min[i]+1;
if (ans1>=3) printf("%d 3\n",ans1); else printf("-1 -1\n");
}

int main()
{
freopen("party10.in","r",stdin);
freopen("out.txt","w",stdout);
Init();
Solve();
return 0;
}



你可能感兴趣的:(2008)