洛谷P1477 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。
输入输出样例
说明
50%的数据,满足n ≤ 300, m ≤ 1000;
100%的数据,满足n ≤ 100000, m ≤ 1000000。
这道题告诉我的惨痛教训 每次挖挖之后一定要看自己空间到底开够没有!!! qswl
这道题是一道很神奇的图论题
首先看到这个关系首先想到把它们建边 这道题主要是分为三种情况
1.环 2.奇奇怪怪的环 3.链
先说说链的情况 如果有环 那么类别数会被环限制 这时候链就没用了 如果没有环 他的类别数就是所有的链长度之和 相当于把所有链的首尾连到一起
接着是环 出现环也就是说总类别数进行了若干次循环 且一定是每个换的因子 所以最大情况数就取所有环的$gcd$即可
最大的问题是奇奇怪怪的环 长这样子的
因为起点相同 所以图中同种颜色的是一个类别的 那么蓝色点相当于和后面两个白点重新成环 为一个三个点的环
所以这种情况怎么处理呢 我们建立边权为$1$的正边 $-1$的反边 那么他走一圈走回蓝点时 要继续往回走就只能走反边
也就是说反边将类别相同的点抵消了 留下了剩余的新环 最后特判一下无解的情况即可
代码
#include <bits/stdc++.h> #define oo 1e9 using namespace std; const int N = 1e6 + 5; int head[N], nex[2 * N], tov[2 * N], val[2 * N]; int vis[N], tot, n, m, sum; queue<int>Q; vector<int>R; void add(int u, int v, int w) { tot ++; nex[tot] = head[u]; tov[tot] = v; val[tot] = w; head[u] = tot; } void Add_Edge( ) { scanf("%d%d",& n,& m); for(int i = 1;i <= m;i ++) { int u, v; scanf("%d%d",& u,& v); add(u, v, 1); add(v, u, -1); } } void bfs(int u) { vis[u] = oo; Q.push(u); bool tag = false; int ma = oo, mi = oo; while(! Q.empty( )) { int u = Q.front( ); Q.pop( ); for(int i = head[u];i;i = nex[i]) { int v = tov[i]; if(! vis[v]) { vis[v] = vis[u] + val[i]; Q.push(v); ma = max(ma, vis[v]); mi = min(mi, vis[v]); } else if(vis[v] == vis[u] + val[i]) continue; else { tag = true; R.push_back(abs(vis[v] - (vis[u] + val[i]))); } } } if(! tag) sum += ma - mi + 1; } int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } void Solve( ) { for(int i = 1; i <= n; i++) { if(! vis[i]) bfs(i); } if(R.size( )) { int ans = R[0]; for(int i = 1;i < R.size( );i ++) { ans = gcd(ans, R[i]); } if(ans <= 2) { printf("-1 -1\n"); return ; } else { int anss = ans; for(int i = 2;i * i <= ans;i ++) { if(ans % i == 0) { if(i >= 3) anss = min(anss, i); if(ans / i >= 3) anss = min(anss, ans / i); } } printf("%d %d\n", ans, anss); } } else { if(sum <= 2) printf("-1 -1\n"); else printf("%d %d", sum, 3); } } int main( ) { Add_Edge( ); Solve( ); }