BZOJ 1064: [Noi2008]假面舞会

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1655  Solved: 798
[Submit][Status][Discuss]

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。

网上的代码+我的注解:

 /*
1.当图中有环时,k必定是环长度的约数,那么答案就是全部环的最大公约数和最小的大于3的公约数
(而且可以看出这个最大公约数一定是这个大于3的最小公约数的倍数,
证明:假设真正的结果是m,因为最大公约数一定是n*m(n>=1),大于三的最小公约数一定是m的约数,
所以这个最大公约数一定是这个大于3的最小公约数的倍数。可以用这个方法从最大值找到最小值),
若最大公约数小于3则无解;
2.当图中没有环时,最小值毫无疑问就是3了,k最大就是所有联通块最长链
(假设每个联通块的最长链都可以接到一起,不能在一个联通块里找两条链,因为他们是有限制关系的,不能接起来)的和。
3技巧:这里面有个技巧,因为如果有两个面具能看见同一个或一个能看见两个,那这两个的一定属于同一类,而且也有可能出现这样的联通块:1->2->3->4->5且6->7->5这样就变得不好处理了。可以把有向边换成无向边正向的话类数+1,反向的话类数-1。这样一来如果找到已经表过号的点就是找到了环,环的长度就是abs(将要编的号-已有编号)。而最长链就是一个联通块内最大编号-最小编号(因为有可能出现负数或0)。
实现时,所有边建长度为1的正向边和长度为-1的反向边,会容易处理很多(这样可以将所有点都标记成到某点距离为多少,可以方便计算环的长度)。 时间复杂度分析:
标号的时间复杂度为O(n+m),枚举的时间复杂度是O(n),找公约数的时间为O(log(n)),所以总时间复杂度为O(nlog(n)+m).
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#define N 200000
#define M 4000000 using namespace std; int head[N],next[M],to[M],len[M];
bool vis[N],bh[M];
int d[N];
int n,m,cnt,ans,tmax,tmin,an; inline void add(int u,int v,int w)
{
to[cnt]=v; len[cnt]=w; next[cnt]=head[u]; head[u]=cnt++;
} inline void read()
{
memset(head,-,sizeof head); cnt=;
scanf("%d%d",&n,&m);
for(int i=,a,b;i<=m;i++)
{
scanf("%d%d",&a,&b);
add(a,b,); add(b,a,-);
}
} inline int gcd(int x,int y)/*ans的初值可以设为0,gcd(0,100)=100,只要把0放在第一位*/
{
int ys;
while(y)
{
ys=x%y;
x=y; y=ys;
}
return x;
} inline void dfs(int u)
{
vis[u]=true;
for(int i=head[u];~i;i=next[i])
{
if(vis[to[i]])/*找到了环*/
{/*计算环的长度并对每个环的长度求最大公约数:abs(d[u]+len[i]-d[to[i]]),这个式子很好理解d[u]+len[i]-d[to[i]],因为标记有可能是负值,所以要取绝对值*/
ans=gcd(ans,abs(d[u]+len[i]-d[to[i]]));
}
else
{
d[to[i]]=d[u]+len[i];
dfs(to[i]);
}
}
} inline void tree(int u)
{/*思路:将一个联通块找到第一个点标记为0,再用这个点找其他的点(不走重边),用所有点的编号的中的max-min+1,就是这个联通块中的结点数目*/
vis[u]=true;
tmax=max(tmax,d[u]);
tmin=min(tmin,d[u]);
for(int i=head[u];~i;i=next[i])
if(!vis[to[i]])
{
bh[i]=bh[i^]=true;
d[to[i]]=d[u]+len[i];
tree(to[i]);
}
} inline void go()
{
for(int i=;i<=n;i++)
if(!vis[i]) dfs(i);/*整张图有可能是森林*/
if(ans)/*如果图中有环的话,那么ans就不是0*/
{
for(an=;an<ans&&ans%an;an++);/*再用ans寻找大于3的(可能等于ans),且能整除ans的,也就是环的最小公约数,是最小*/
}
else/*没有环的情况*/
{
memset(vis,,sizeof vis);
for(int i=;i<=n;i++)
if(!vis[i])/*最大是每个联通块中的最长链的长度和,没找到一个!vis[i],就是一个联通块*/
{
tmax=tmin=d[i]=;
tree(i);
ans+=tmax-tmin+;
}
an=;/*最小就是3了*/
}
if(ans<) ans=an=-;/*注意要把这个ans小于3,放在最后面,因为可能在没有环的情况下,把各个联通块的最长路径加在一起也超不过3,比如那种极端的网状图,最长路径就有可能是1了,所以要把这个ans<3放在外面。*/
/*我一开始就是仅仅把ans<3放到了有环的判断中,结果错了一个点*/
printf("%d %d\n",ans,an);
} int main()
{
read();go();
return ;
}

我的代码:

 #define N 100010
#define M 1000100
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#include<cstdio>
int n,m,ans=,an,head[N],t=-,d[N];
bool vis[N]={},bianflag[M<<];
struct Edge{
int v,w,last;
}edge[M<<];
int read1()
{
int ret=,ff=;
char s=getchar();
while(s<''||s>'')
{
if(s=='-') ff=-;
s=getchar();
}
while(s>=''&&s<='')
{
ret=ret*+s-'';
s=getchar();
}
return ret*ff;
}
void add_edge(int u,int v,int w)
{
++t;
edge[t].v=v;
edge[t].w=w;
edge[t].last=head[u];
head[u]=t;
}
void input()
{
n=read1();m=read1();
int a,b;
memset(head,-,sizeof(head));
for(int i=;i<=m;++i)
{
scanf("%d%d",&a,&b);
add_edge(a,b,);
add_edge(b,a,-);
}
}
int gcd(int a,int b)
{
if(!b) return a;
return gcd(b,a%b);
}
void dfs1(int k)
{
vis[k]=true;
for(int l=head[k];l!=-;l=edge[l].last)
{
if(vis[edge[l].v])
{
ans=gcd(ans,abs(d[k]+edge[l].w-d[edge[l].v]));
}
else {
d[edge[l].v]=d[k]+edge[l].w;
dfs1(edge[l].v);
}
}
}
void dfs2(int k,int &maxx,int &minn)
{
vis[k]=true;
maxx=max(maxx,d[k]);
minn=min(minn,d[k]);
for(int l=head[k];l!=-;l=edge[l].last)
{
if(bianflag[l]) continue;
bianflag[l]=true;
bianflag[l^]=true;
d[edge[l].v]=d[k]+edge[l].w;
dfs2(edge[l].v,maxx,minn);
}
}
int main()
{
input();
for(int i=;i<=n;++i)
{
if(!vis[i]) dfs1(i);
}
if(ans)
{
for(an=;an<ans&&ans%an;++an); }
else
{
an=;
memset(vis,false,sizeof(vis));
for(int i=;i<=n;++i)
{
if(!vis[i])
{
int maxx,minn;
maxx=minn=d[i]=;
dfs2(i,maxx,minn);
ans+=maxx-minn+;
//cout<<maxx<<" "<<minn<<" "<<ans<<endl;
}
}
}
if(ans<)
{
ans=-;an=-;
}
printf("%d %d",ans,an);
return ;
}

图论 公约数 找环和链 BZOJ [NOI2008 假面舞会]的更多相关文章

  1. BZOJ 1064 假面舞会(NOI2008) DFS判环

    此题,回想Sunshinezff学长给我们出的模拟题,原题啊有木有!!此处吐槽Sunshinezff爷出题不人道!! 不过也感谢Sunshinezff学长的帮助,我才能做出来.. 1064: [Noi ...

  2. 【做题记录】[NOI2008] 假面舞会—有向图上的环与最长链

    luogu 1477 [NOI2008] 假面舞会 容易发现: 如果图中没有环,那么面具种数一定是所有联通块内最长链之和,最少为 \(3\) . 如果有环,则面具种数一定是所有环的大小的最大公约数. ...

  3. 【洛谷】1477:[NOI2008]假面舞会【图论】

    P1477 [NOI2008]假面舞会 题目描述 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会. 今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具 ...

  4. [BZOJ1064][Noi2008]假面舞会

    [BZOJ1064][Noi2008]假面舞会 试题描述 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会.今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择一 个自己喜欢 ...

  5. 【BZOJ1064】[Noi2008]假面舞会 DFS树

    [BZOJ1064][Noi2008]假面舞会 Description 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会.今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择 ...

  6. NOI2008假面舞会

    1064: [Noi2008]假面舞会 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 883  Solved: 462[Submit][Status] ...

  7. 【BZOJ】1064: [Noi2008]假面舞会(判环+gcd+特殊的技巧)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1064 表示想到某一种情况就不敢写下去了.... 就是找环的gcd...好可怕.. 于是膜拜了题解.. ...

  8. BZOJ1064 NOI2008 假面舞会 图论

    传送门 将一组关系\((A,B)\)之间连一条边,那么显然如果图中存在环长为\(len\)的环,那么面具的种数一定是\(len\)的因数. 值得注意的是这里环的关系除了\(A \rightarrow ...

  9. 【图论 搜索】bzoj1064: [Noi2008]假面舞会

    做到最后发现还是读题比赛:不过还是很好的图论题的 Description 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会.今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选 ...

随机推荐

  1. 第二章--Win32程序运行原理 (部分概念及代码讲解)

    学习<Windows程序设计>记录 概念贴士: 1. 每个进程都有赋予它自己的私有地址空间.当进程内的线程运行时,该线程仅仅能够访问属于它的进程的内存,而属于其他进程的内存被屏蔽了起来,不 ...

  2. (旧)子数涵数·Flash——影片剪辑的其他操作

    一.复制影片剪辑 1.方法:duplicatemovieClip(影片实名,新实名,深度级别) 2.解释:影片实名就是你要复制的对象,新实名就是你要粘贴的对象,深度级别就是粘贴后的影片剪辑的堆叠顺序( ...

  3. 中国各城市PM2.5数据间的相关分析

    code{white-space: pre;} pre:not([class]) { background-color: white; }if (window.hljs && docu ...

  4. CSS应用内容补充及小实例

    一.clear 清除浮动 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...

  5. [性能] Bean拷贝工具类性能比较

    Bean拷贝工具类性能比较 引言 几年前做过一个项目,接入新的api接口.为了和api实现解耦,决定将api返回的实体类在本地也建一个.这样做有两个好处 可以在api变更字段的时候保持应用稳定性 可以 ...

  6. 【GOF23设计模式】状态模式

    来源:http://www.bjsxt.com/ 一.[GOF23设计模式]_状态模式.UML状态图.酒店系统房间状态.线程对象状态切换 package com.test.state; public ...

  7. [CLK Framework] CLK.Settings - 跨平台的参数存取模块

    [CLK Framework] CLK.Settings - 跨平台的参数存取模块 问题情景 开发功能模块的时候,常常免不了有一些参数(例如ConnectionString),需要存放在Config檔 ...

  8. 原生js验证简洁美观注册登录页面

    序 一个以js验证表单的简洁的注册登录页面,不多说直接上图 效果 主要文件 完整代码 sign_up.html 注册表单 <!DOCTYPE html> <html lang=&qu ...

  9. mac下用ruby安装sass && webstorm下给scss文件添加watch

    1.安装rvm 先安装 [Xcode](http://developer.apple.com/xcode/) 开发工具,它将帮你安装好 Unix 环境需要的开发包 sudo curl -L https ...

  10. C#中如何排除/过滤/清空/删除掉字符串数组中的空字符串

    C#中要如何才能删除一个字符串数组中的空字符串呢?随着微软对C#不断发展和更新,C#中对于数组操作的方式也变得越来越多样化.以往要实现过滤数组中的空字符串,都是需要实行循环的方式来排除和过滤.C#3. ...