• 假面舞会 题解


    题目描述

    一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会。

    今年的面具都是主办方特别定制的。每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具。每个面具都有一个编号,主办方会把此编号告诉拿该面具的人。为了使舞会更有神秘感,主办方把面具分为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

    数据范围与提示

    50%的数据,满足n ≤ 300, m ≤ 1000;
    100%的数据,满足n ≤ 100000, m ≤ 1000000。

    分析

    根据题目的意思:

    1. 如果有多个点指向同一个点,那么他们属于同一类别。
    2. 同一个点看到的所有点是一个种类。

    按照上面的结论,我们可以把同类别的所有的点缩成一个点,这样对于一个连通块(不一定是强联通),就可以变成若干环和若干链的形式。

    • 对于链来说,种类数为多少都是可以满足的;
    • 对于环来说,如果只有一个环(假设点的个数不小于3),那么环的大小即为最大可能的种类数,同时,环的大小的所有的约数也可以是种类数;那么对于多个环的情况,最大种类数就只能是所有环大小的最大公约数,同时小于最大公约的所有的约数也是可能的种类数。如果没有环,那么最大种类数就是所有链的长度之和。

    因此,最终的答案为:

    • 最大值:如果有环,就是所有环中点的个数的最大公约数;如果无环,就是所有链的长度之和
    • 最小值:如果有环,答案为不小于3的,同时是所有环中点的个数的最小的约数;如过无环,答案为3

    为了找环和链,我们可以针对每个关系建立一条正向边和反向边,权值分别用1和-1来表示,从某一起点出发记录到达任意一点的权值(路径上的边权和),根据权值来求解环的大小和链的长度,具体看代码注释

    int main() {
    	scanf("%d%d", &n, &m);
    	memset(head, -1, sizeof(head));
    	for (int i = 1; i <= m; i++) {
    		int x, y;
    		scanf("%d%d", &x, &y);
    		// 正向反向各加一条边
    		addedge(x, y, 1);
    		addedge(y, x, -1);
    	}
    	// 先用DFS找环求解
    	memset(vis, 0, sizeof(vis));
    	for (int i = 1; i <= n; i++) {
    		if (!vis[i]) DFS(i);
    	}
    	// 找环结束,根据得到的最大公约数判断
    	if (ans) { // ans初始为0,如果之前没找到环,ans仍会为0
    		if (ans < 3) printf("-1 -1
    "); // 最大公约数小于3,不成立
    		else { // 这就成立了,再找最小的答案,即最小的约数
    			int x;
    			for (x = 3; x <= ans; x++) if (ans % x == 0) break;
    			printf("%d %d
    ", ans, x);
    		}
    		return 0;
    	}
    
    	// 找环失败了,找链
    	memset(vis, 0, sizeof(vis));
    	for (int i = 1; i <= n; i++) {
    		if (!vis[i]) {
    			mx = mn = d[i] = 0; // 起点初始化好就可以了
    			dfs(i);
    			ans += mx - mn + 1; // 找到一条链中最大距离和最小距离,做差即为链长
    		}
    	}
    	if (ans >= 3) printf("%d %d
    ", ans, 3);
    	else printf("-1 -1
    ");
    	return 0;
    }
    
    // 再来看看两个dfs
    // 第一个处理环
    void DFS(int now) {
    	vis[now] = 1;
    	for (int i = head[now]; ~i; i = e[i].next) {
    		int to = e[i].to;
    		if (!vis[to]) {
    			d[to] = d[now] + e[i].w; // 更新起点到to的距离
    			DFS(to);
    		}
    		else {
    			// 通过正向边或者反向边找到一个环,求gcd更新答案
    			ans = gcd(ans, abs(d[now] + e[i].w - d[to]));
    		}
    	}
    }
    
    // 第二个处理链
    void dfs(int now) {
    	// 更新链中最大和最小权值
    	mx = max(mx, d[now]);
    	mn = min(mn, d[now]);
    	vis[now] = 1;
    	for (int i = head[now]; ~i; i = e[i].next) {
    		if (!flag[i]) { // 如果此边没走过,则双向都标记,让它一路走到黑,防止往回走
    			flag[i] = flag[i ^ 1] = 1;
    			int to = e[i].to;
    			d[to] = d[now] + e[i].w;
    			dfs(to);
    		}
    	}
    }
    
  • 相关阅读:
    memset()函数,多用于清空数组
    Echart 词云图 上手代码 同含(echarts-wordcloud.js)最简单的教程 复制可用
    爬虫使用真实浏览器打开网页进行爬取
    jsoup 模拟登陆github网页(源代码)亲测可用 直接复制就能用
    拷贝虚拟电脑 Ubuntu 系统 含hadoop hive hbase mysql spark eclipse
    Python 连接MySQL 增删改查 直接可用(最简易,含源码)
    Python 中文词频统计,热词统计,简要分析(含上手源码)
    百度百科简介爬取(含源代码、信息领域词频数据csv格式)
    博客园博文爬取 标签爬取(含源代码)
    输入一行电报文字,将字母变成其下一字母
  • 原文地址:https://www.cnblogs.com/kuangbiaopilihu/p/13304530.html
Copyright © 2020-2023  润新知