[Noi2008]假面舞会

来源:互联网 发布:淘宝名店 编辑:程序博客网 时间:2024/05/17 06:44

考试的时候果断放弃,cout<<"-1 -1"骗10分hhh。。。

这也是图上问题。注意题目意思:

①如果有多个点指向同一个点,那么他们属于同一类别。

②一个点看到的所有点是一个种类。

这样的话,就可以把信息变成一堆图和一堆链。注意分情况:

①如果全是链的话,那么种类最大是他们的长度,最小理论上是多少都可以。例如1->2->3->4->5->6,可以6个种类都不同,也可能1,2,3种类不同;4,5,6种类不同,3(种类3)可以看到4(种类1),实际是一个循环,但是图中是一个链。这样种类数可以随便划分。根据题意最小就是3。

②如果有环有链,那就只需要考虑环就行了。环最大种类是环的大小,同时这个环的约数也是可以满足的

综上,总结为:

所以如果有环找出这些环,k的最大值就是这些环的大小的最大公约数。

            k的最小值就是k的最大值中第一个大于等于3的约数。

            如果没有环,k的最大值就是所有等价链的链长之和。最小值显然是3.


注意环的大小与方向有关,反向是-1,正向是1,最后取abs即可。

  然后我们dfs每个联通块。在dfs途中对每个点标号,标号的值就是已经经过的权值和。

            在dfs中如果发现某个点已经被dfs过了。说明找到一个环。

            那么这个环的大小就是你将要对他标的号和他已有的标号的差的绝对值。 

#include<iostream>#include<cstdio>#include<cstring>#include<set>#define pos(i,a,b) for(int i=(a);i<=(b);i++)#define pos2(i,a,b) for(int i=(a);i>=(b);i--)using namespace std;int n,m;#define N 501000int fa[N];int mi[N],ma[N];int tmp;int abs(int x){    if(x<0)      return -x;    return x;}struct haha{       int next,w,to;}edge[N];int head[N],cnt=1;int val[N],flag[N];int gcd(int x,int y){    return y==0?x:gcd(y,x%y);} //求最大公约数 void add(int u,int v,int w){     edge[cnt].w=w;     edge[cnt].to=v;     edge[cnt].next=head[u];     head[u]=cnt++;}int find(int x){    if(x!=fa[x])      fa[x]=find(fa[x]);    return fa[x];}void he(int x,int y){     int xx=find(x);     int yy=find(y);     if(xx!=yy)       fa[xx]=yy;}//并查集查找是否属于一个联通块 int ans,ans2;//最大值,最小值 void dfs(int x){     ma[tmp]=max(ma[tmp],val[x]);//找连通块里面的最大值      mi[tmp]=min(mi[tmp],val[x]); //最小值      flag[x]=1;     for(int i=head[x];i;i=edge[i].next)     {        int to=edge[i].to;        int va=edge[i].w;        if(!flag[to])        {          val[to]=val[x]+va;//记录大小           dfs(to);        }        else           ans=gcd(ans,abs(val[x]+va-val[to]));// 所有环求公约数      }}int main(){    scanf("%d%d",&n,&m);    memset(mi,0x3f,sizeof(mi));    pos(i,1,n)      fa[i]=i;    pos(i,1,m)    {       int x,y;       scanf("%d%d",&x,&y);       add(x,y,1);       add(y,x,-1);       he(x,y);    }    pos(i,1,n)      if(!flag[i])      {         tmp=find(i);         dfs(i);      }    pos(i,3,ans)      if(ans%i==0)      {        ans2=i;//大于三的最小公约数         break;      }    ans2=max(3,ans2);    int sum=0;    if(ans==0)    {       pos(i,1,n)         if(i==fa[i])           sum+=ma[i]-mi[i]+1;//是链最大是总和        ans=sum;    }    if(ans<3)      ans=ans2=-1;    printf("%d %d\n",ans,ans2);    while(1);    return 0;}