标签:
[BZOJ1064][Noi2008]假面舞会
试题描述
一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会。今年的面具都是主办方特别定制的。每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具。每个面具都有一个编号,主办方会把此编号告诉拿该面具的人。为了使舞会更有神秘感,主办方把面具分为k (k≥3)类,并使用特殊的技术将每个面具的编号标在了面具上,只有戴第i 类面具的人才能看到戴第i+1 类面具的人的编号,戴第k 类面具的人能看到戴第1 类面具的人的编号。 参加舞会的人并不知道有多少类面具,但是栋栋对此却特别好奇,他想自己算出有多少类面具,于是他开始在人群中收集信息。 栋栋收集的信息都是戴第几号面具的人看到了第几号面具的编号。如戴第2号面具的人看到了第5 号面具的编号。栋栋自己也会看到一些编号,他也会根据自己的面具编号把信息补充进去。由于并不是每个人都能记住自己所看到的全部编号,因此,栋栋收集的信 息不能保证其完整性。现在请你计算,按照栋栋目前得到的信息,至多和至少有多少类面具。由于主办方已经声明了k≥3,所以你必须将这条信息也考虑进去。
输入
第一行包含两个整数n, m,用一个空格分隔,n 表示主办方总共准备了多少个面具,m 表示栋栋收集了多少条信息。接下来m 行,每行为两个用空格分开的整数a, b,表示戴第a 号面具的人看到了第b 号面具的编号。相同的数对a, b 在输入文件中可能出现多次。
输出
包含两个数,第一个数为最大可能的面具类数,第二个数为最小可能的面具类数。如果无法将所有的面具分为至少3 类,使得这些信息都满足,则认为栋栋收集的信息有错误,输出两个-1。
输入示例1
6 5 1 2 2 3 3 4 4 1 3 5
输出示例1
4 4
输入示例2
3 3 1 2 2 1 2 3
输出示例2
-1 -1
数据规模及约定
100%的数据,满足n ≤ 100000, m ≤ 1000000。
题解
先看看我们知道什么。假设一个合法的答案是 k,则给了一条有向边(a, b)后,就说明 a 在第 i 组且 b 在第 i + 1 组,或者是 a 在第 k 组且 b 在第 1 组。
不难想到当给定的有向边形成一个环之后,k 一定是这个环长度的约数(因为它可以从第 1 组某个节点连到第 2 组某个节点,一直连到第 k 组某个节点,再从第 k 组的那个节点直接连一条边到第 1 组的某个节点,如此往复最终首尾相接,则这个环绕了若干圈这 k 个组)。
还有一种情况:a -> b -> c -> d -> e 且 a -> e,我们可以尝试给它分组,先考虑前面那条链,可以暂时将它们分成这个样子:a∈1,b∈2,c∈3,d∈4,e∈5,那么再加上 a -> e 这条边后发现 e 的编号应该等于 a 的编号加 1,所以将其与 b 分到第 2 组,同理 a 的编号应该等于原来 e 的编号减 1,所以将其与 d 分到第 4 组,最终结果是:b, e∈2,c∈3,a, d∈4,最终组数为 3。
综上两点可以得出一种做法:从每个连通分量的一个节点开始标号,顺着有向边走时标号加 1,逆着走时减 1,发现当前结点有标号时,则最大答案就是所有这种情况中的“原标号与新标号之差的绝对值”取最大公约数,最小答案就是所有所有这样的值取大于等于 3 的最小公约数。
注意一个特判,当给的图在以上两种情况之外,即将有向边变成无向边后没有圈的图(成为一个森林)时,每个连通分量的最大标号 - 最小标号 + 1,求和就是最大答案,最小答案是 3(若合法).
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); } return x * f; } #define maxn 100010 #define maxm 2000010 #define oo 2147483647 int n, m, head[maxn], next[maxm], to[maxm], dist[maxm], ans; void AddEdge(int a, int b) { to[++m] = b; dist[m] = 1; next[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; dist[m] = -1; next[m] = head[a]; head[a] = m; return ; } int gcd(int a, int b) { return !b ? a : gcd(b, a % b); } int mark[maxn], mx, mn; bool vis[maxn]; void dfs(int u, int x) { if(vis[u]){ ans = gcd(ans, abs(mark[u] - x)); return ; } vis[u] = 1; mark[u] = x; mx = max(mx, x); mn = min(mn, x); for(int e = head[u]; e; e = next[e]) dfs(to[e], x + dist[e]); return ; } int main() { n = read(); int m = read(); for(int i = 1; i <= m; i++) { int a = read(), b = read(); AddEdge(a, b); } int tmp = 0; for(int i = 1; i <= n; i++) if(!vis[i]) { mx = -oo; mn = oo; dfs(i, 1); tmp += mx - mn + 1; } int a2; if(!ans) ans = tmp, a2 = 3; else for(a2 = 3; a2 <= ans && ans % a2; a2++) ; if(ans < 3) puts("-1 -1"); else printf("%d %d\n", ans, a2); return 0; }
标签:
原文地址:http://www.cnblogs.com/xiao-ju-ruo-xjr/p/5595287.html