【图论 搜索】bzoj1064: [Noi2008]假面舞会
做到最后发现还是读题比赛;不过还是很好的图论题的
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。
题目分析
naive
首先会有一个很naive的想法:
对于无环的图找最大值:拓扑地从1开始标号dfs做下去,中途检查边$(u,v)$,若$v$已经被标号且$col_v≠col_u+1$,就是不合法的,随即输出"-1 -1"。

讲上去求的是最大值所以看上去很对劲是吧?

但是遇上这么一个环呢?7之后连的是2,所以判成无解。但是然而实际上$k=3$是成立的。
也就是说,简单地考虑“环缩点”或者“直接染色”是行不通的。
实际做法
学了tarjan之后不要在找环只想到tarjan!
这个问题其实建个反向边之后,可以分为两类:
- 有环的
- 没环的
没环的情况:树是很简单的,最大值就是所有树的最长链总和,最小值一定是3。
有环的情况:
DFS听上去很基础吧,但是切不要以为基础的东西就没什么用处。
这里依靠DFS找出每一个环的长度,并且注意到最大答案就是所有环长度的gcd。只要最终的$gcd≥3$,结合没环情况的下界可知环外其他树对答案不造成影响。
可能会想到环套环的情况。不过首先这个DFS要永久标记访问,复杂度是$O(n)$的,不会被卡;其次我们求的是环长度的gcd,并且如果答案合法,大环长度一定是小环的倍数,所以即便大环套在小环外,也不影响最终答案。
大致思路就是这样。
细节注意树的情况,最大值是所有树的最长链总和!
#include<bits/stdc++.h>
const int maxn = ;
const int maxm = ; struct node
{
int id;
std::vector<int> norEdge,difEdge;
}a[maxn];
int n,m,cnt,mn,mx,chain,ans;
bool vis[maxn];
std::pair<int, int> edges[maxm]; int read()
{
char ch = getchar();
int num = ;
bool fl = ;
for (; !isdigit(ch); ch = getchar())
if (ch=='-') fl = ;
for (; isdigit(ch); ch = getchar())
num = (num<<)+(num<<)+ch-;
if (fl) num = -num;
return num;
}
int gcd(int x, int y){return !y?x:gcd(y, x%y);}
void addedge(int x)
{
int u = edges[x].first, v = edges[x].second;
a[u].norEdge.push_back(v), a[v].difEdge.push_back(u);
}
void dfs(int x, int lb)
{
if (vis[x]){
ans = gcd(ans, abs(a[x].id-lb)); //找到一个环了
return;
}
a[x].id = lb, vis[x] = ;
mn = std::min(mn, lb), mx = std::max(mx, lb);
int sa = a[x].norEdge.size(), sb = a[x].difEdge.size();
for (int i=; i<sa; i++)
dfs(a[x].norEdge[i], lb+); //正向边
for (int i=; i<sb; i++)
dfs(a[x].difEdge[i], lb-); //反向边
return;
}
int main()
{
// freopen("testdata.in","r",stdin);
n = read(), m = read();
for (int i=; i<=m; i++)
edges[i].first = read(), edges[i].second = read();
std::sort(edges+, edges+m+); //用pair存边方便去重
addedge();
for (int i=; i<=m; i++)
if (edges[i]!=edges[i-])
addedge(i);
for (int i=; i<=n; i++)
if (!vis[i]){ //由于建了双向边,故不用考虑拓扑序
dfs(i, );
chain += mx-mn+;
mn = mx = ;
}
if (ans >= ){ //如果有环并且合法
for (int i=; i<=ans/; i++)
if (ans%i==){
printf("%d %d\n",ans,i); //找最小的答案——求最小约数
return ;
}
printf("%d %d\n",ans,ans); //最小答案还是ans
return ;
}
if (!ans&&chain>=){
printf("%d %d\n",chain,);
return ;
} //否则不合法,ans=1
printf("-1 -1\n");
return ;
}
END
【图论 搜索】bzoj1064: [Noi2008]假面舞会的更多相关文章
- [BZOJ1064][Noi2008]假面舞会
[BZOJ1064][Noi2008]假面舞会 试题描述 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会.今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择一 个自己喜欢 ...
- BZOJ1064 [Noi2008]假面舞会 【dfs】
题目 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会.今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具.每个面具都有一个编号,主办方会把此编号告诉拿 ...
- BZOJ1064 NOI2008 假面舞会 图论
传送门 将一组关系\((A,B)\)之间连一条边,那么显然如果图中存在环长为\(len\)的环,那么面具的种数一定是\(len\)的因数. 值得注意的是这里环的关系除了\(A \rightarrow ...
- BZOJ1064 NOI2008假面舞会(dfs树)
将图中的环的长度定义为正向边数量-反向边数量,那么答案一定是所有环的环长的共同因子.dfs一下就能找到图中的一些环,并且图中的所有环的环长都可以由这些环长加加减减得到(好像不太会证).如果有环长为1或 ...
- BZOJ1064 NOI2008假面舞会
挺神的这题,发现只有环和链两种情况 搜索时我们只考虑环的,因为链可以看成找不到分类的环. 当成链时大小是的最大值是各链长的和,最小值是3 当成环时最大值是各环长的gcd,最小值是大于3的最小的ans的 ...
- 【BZOJ1064】[Noi2008]假面舞会 DFS树
[BZOJ1064][Noi2008]假面舞会 Description 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会.今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择 ...
- 图论 公约数 找环和链 BZOJ [NOI2008 假面舞会]
BZOJ 1064: [Noi2008]假面舞会 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1655 Solved: 798[Submit][S ...
- 【洛谷】1477:[NOI2008]假面舞会【图论】
P1477 [NOI2008]假面舞会 题目描述 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会. 今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具 ...
- NOI2008假面舞会
1064: [Noi2008]假面舞会 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 883 Solved: 462[Submit][Status] ...
随机推荐
- C# 基础之类与结构体的区别
1.语法上的区别是: 定义类使用关键字class,定义结构体用关键字struct 2.结构体中不可对声明字段进行初始化,但类可以 3. 类:如果没有为类显示的定义一个构造函数,c#编译器会自动生成一个 ...
- bzoj 5249 [2018多省省队联测] IIIDX
bzoj 5249 [2018多省省队联测] IIIDX Link Solution 首先想到贪心,直接按照从大到小的顺序在后序遍历上一个个填 但是这样会有大问题,就是有相同的数的时候,会使答案不优 ...
- JAVA常用知识总结(十二)——数据库(二)
MySQL主从热备份工作原理 简单的说:就是主服务器上执行过的sql语句会保存在binLog里面,别的从服务器把他同步过来,然后重复执行一遍,那么它们就能一直同步啦. 整体上来说,复制有3个步骤: 作 ...
- axios delete 请求
axios delete 请求 在传递一个参数的时候,直接把参数放在请求连接后面,用'/' 连接就可以了 this.axios.post(this.APIURL+'/'+ID) //http://ww ...
- 【转】Effective C#观后感之提高Unity中C#代码质量的21条准则
转自:http://blog.csdn.net/swj524152416/article/details/75418162 我们知道,在C++领域,作为进阶阅读材料,必看的书是<Effectiv ...
- centos7安装mysql5.7 使用yum
https://blog.csdn.net/z13615480737/article/details/78906598 使用yum,比较简单,不用考虑版本依赖问题
- js实现文本框验证和实现小数的加减乘除
<script type="text/javascript"> //加法 var m=accAdd(1.22,1.22); //减法 var m1=accSub(1.2 ...
- kafka基础二
kafka生产者工作流程 消息产生分析 1.写入方式: producer采用推(push)模式将消息发布到broker,每条消息都会被追加(append)到分区Partition上,属于顺序写磁盘(顺 ...
- 【笨办法学Python】习题11:打印出改变了的输入
print "How old are you?", age = raw_input() print "How tall are you?", height = ...
- 使用 PHPUnit 和 Selenium 进行测试
适用于 PHP 的 NetBeans IDE 支持 PHPUnit 自动测试.通过 PHPUnit,NetBeans IDE 可为 PHP 提供代码覆盖率,这与 IDE 为 Python 提供的代码覆 ...