【bzoj1064】【NOI2008】【假面舞会】【dfs】

Description

一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会。今年的面具都是主办方特别定制的。每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具。每个面具都有一个编号,主办方会把此编号告诉拿该面具的人。为了使舞会更有神秘感,主办方把面具分为k (k≥3)类,并使用特殊的技术将每个面具的编号标在了面具上,只有戴第i 类面具的人才能看到戴第i+1 类面具的人的编号,戴第k 类面具的人能看到戴第1 类面具的人的编号。 参加舞会的人并不知道有多少类面具,但是栋栋对此却特别好奇,他想自己算出有多少类面具,于是他开始在人群中收集信息。 栋栋收集的信息都是戴第几号面具的人看到了第几号面具的编号。如戴第2号面具的人看到了第5 号面具的编号。栋栋自己也会看到一些编号,他也会根据自己的面具编号把信息补充进去。由于并不是每个人都能记住自己所看到的全部编号,因此,栋栋收集的信 息不能保证其完整性。现在请你计算,按照栋栋目前得到的信息,至多和至少有多少类面具。由于主办方已经声明了k≥3,所以你必须将这条信息也考虑进去。

Input

第一行包含两个整数n, m,用一个空格分隔,n 表示主办方总共准备了多少个面具,m 表示栋栋收集了多少条信息。接下来m 行,每行为两个用空格分开的整数a, b,表示戴第a 号面具的人看到了第b 号面具的编号。相同的数对a, b 在输入文件中可能出现多次。

Output

包含两个数,第一个数为最大可能的面具类数,第二个数为最小可能的面具类数。如果无法将所有的面具分为至少3 类,使得这些信息都满足,则认为栋栋收集的信息有错误,输出两个-1。

Sample Input

【输入样例一】

6 5
1 2
2 3
3 4
4 1
3 5

【输入样例二】

3 3
1 2
2 1
2 3

Sample Output

【输出样例一】
4 4

【输出样例二】
-1 -1

HINT

100%的数据,满足n ≤ 100000, m ≤ 1000000。

题解:首先从一个点连出去的点标号肯定一样。

            同理如果几个点指向同一个点。那他们的标号也一样。

            把这些关系收缩一下。那么图中就会形成一些环和一些链。

            对于链,无论k等于几都是可以满足的。

            对于环。满足这个环的k的最大值是它的大小。

            同时这个环的大小的约数也是可以满足的。

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

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

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

            为了找到这些环。我们把每条边拆成两条。以前的边权值为1,新加的边权值为-1.

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

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

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

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100010
#define M 1000010
using namespace std;
int n,m,x,y,p[N],fa[N],maxx[N],minn[N],point[N],next[2*M],cnt,r1,r2,ans,ans2,temp;
bool f[N];
struct use{int st,en,v;}e[M*2];
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int abs(int x){if (x<0) return -x;else return x;}
void add(int x,int y,int w){
    next[++cnt]=point[x];point[x]=cnt;
    e[cnt].st=x;e[cnt].en=y;e[cnt].v=w;
}
int find(int x){if (x!=fa[x]) fa[x]=find(fa[x]);return fa[x];}
void dfs(int x){
   maxx[temp]=max(maxx[temp],p[x]);minn[temp]=min(minn[temp],p[x]);f[x]=true;
   for (int i=point[x];i;i=next[i]){
       if (!f[e[i].en]){p[e[i].en]=p[x]+e[i].v;dfs(e[i].en);}
       else ans=gcd(ans,abs(p[x]+e[i].v-p[e[i].en]));
    }   
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) fa[i]=i;memset(minn,127/3,sizeof(minn));
    for (int i=1;i<=m;i++){
       scanf("%d%d",&x,&y);
       add(x,y,1);add(y,x,-1);
       r1=find(x);r2=find(y);
       if (r1!=r2) fa[r1]=r2;
    }
    for (int i=1;i<=n;i++) if (!f[i]) {temp=find(i);dfs(i);}
    for (int i=3;i<=ans;i++) if (ans%i==0){ans2=i;break;}
    if (ans2<3) ans2=3;temp=0;
    if (ans==0) for (int i=1;i<=n;i++) if (i==fa[i]) temp+=maxx[i]-minn[i]+1;    
    if (ans==0) ans=temp;
    if (ans<3) {ans=-1;ans2=-1;}
    printf("%d %d\n",ans,ans2);
}


  • 0
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

sunshinezff

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值