传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1064
一道神奇的题目,将人们之间的关系看成边,把它变成无向图,那么我们可以发现若原图没有环,那么每个联通块的面具的数目是任意的,所以此时最大为每个联通块的最长链之和,最小为3,
现在我们来考虑有环的情况,可以发现,环上的节点数量一定是答案的倍数,若有多个环,就为答案的公约数。那么我们怎么来计算每个环的节点个数呢,这就可以用一种神奇的标号法来解决,将正向边的权值设为1,反向边的权值设为-1,每个点有一个顶标,那么我们从任意一个点开始dfs,顶标记录经过的边权和,那么一个环的长度就为第二次到达该节点的顶标与第一次到达该节点的顶标之差的绝对值,这应该还是不难想通。
于是就这样了。。。
#include <iostream> #include <cstdio> #include <cstring> #include <list> using namespace std; #define Abs(x) ((x) > 0 ? (x) : (-(x))) const int maxn = 100010; struct node { int x, val; node() {} node(int a, int b) { x = a; val = b; } }; list <node> edge[maxn]; bool vis[maxn]; int d[maxn]; int n, m, ans, ans2, mx, mi; void get(int &tmp) { tmp = 0; char ch = getchar(); for(; ch < '0' || ch > '9'; ch = getchar()); for(; ch >= '0' && ch <= '9'; ch = getchar()) tmp = tmp * 10 + ch - '0'; return; } int gcd(int a, int b) { if(!b) return a; else return gcd(b, a % b); } void dfs(int x) { vis[x] = true; for(list <node> :: iterator p = edge[x].begin(); p != edge[x].end(); p ++) { if(vis[p -> x]) { ans = gcd(ans, Abs(d[x] + p -> val - d[p -> x])); } else { d[p -> x] = d[x] + p -> val; dfs(p -> x); } } return; } void dfs2(int x) { vis[x] = true; mx = max(mx, d[x]); mi = min(mi, d[x]); for(list <node> :: iterator p = edge[x].begin(); p != edge[x].end(); p ++) { if(!vis[p -> x]) { d[p -> x] = d[x] + p -> val; dfs2(p -> x); } } return; } int main() { get(n); get(m); int a, b; for(int i = 1; i <= m; i ++) { get(a); get(b); edge[a].push_back(node(b, 1)); edge[b].push_back(node(a, -1)); } for(int i = 1; i <= n; i ++) { if(!vis[i]) dfs(i); } if(ans) { for(ans2 = 3; ans2 < ans && ans % ans2; ans2 ++); } else { memset(vis, false, sizeof(vis)); memset(d, 0, sizeof(d)); for(int i = 1; i <= n; i ++) { if(!vis[i]) { mx = 0; mi = 0; dfs2(i); ans += mx - mi + 1; } } ans2 = 3; } if(ans < 3) printf("-1 -1\n"); else { printf("%d %d\n", ans, ans2); } return 0; }