zoukankan      html  css  js  c++  java
  • [BZOJ1064][Noi2008]假面舞会

    [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
    ", ans, a2);
    	
    	return 0;
    }
    
  • 相关阅读:
    event.relatedTarget、event.fromElement、event.toElement
    before/after伪类常见用法
    $.getJSON 跨域
    ExtJS中store.findExact
    C#生成6位随机验证码
    C#验证手机号
    jQuery轮播图的事项 代码详细,容易理解。。。。谢谢观赏
    ES6 中 static 的this 丢失问题解决办法 简单明了 备注清晰 谢谢欣赏
    ES6 中 static 的this 问题 简单明了 备注清晰 谢谢欣赏
    ES6 中用class创建img 详细明了,步骤清晰,解释完美,谢谢欣赏
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5595287.html
Copyright © 2011-2022 走看看