1 条题解
-
0
自动搬运
来自洛谷,原作者为

StudyingFather
在纷繁杂乱的世界里,独自寻找属于自己的光荣与梦想。搬运于
2025-08-24 21:25:32,当前版本为作者最后更新于2019-12-29 23:58:00,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
(UPD 2021/08/21:感谢
https://www.luogu.com.cn/user/317459对相关部分进行了修正。)如果整个图没有环,且不存在两条共用起点和终点的相交链,显然最多能分的种类数是每个连通分量内最长链的长度之和。
如果整个图是由若干个不相交的环构成的话,最多能分的种类数是所有环长度的最大公约数(找环的时候,可以从连通块内的任意一点开始编号,第二次经过一个点的时候,它第二次的编号减去第一次的编号就是环的大小)。
除了这两种特殊情况之外,还有两种情况:
- 两个环之间有公共部分(指至少共用两个点)。
- 存在两个链共用起点和终点。
对于情况 1,合法的面具数一定是这两个环长度的公约数。
对于情况 2,合法的面具数一定是两个链长度差的约数。
我们将每个部分的结果合并,最后就可以得到整个图的结果。
先处理情况 1。我们考虑倒推:建图的时候不仅连一条 ,边权为 的边之外,同时连一条 ,边权为 的边(这种连边方式可以确保我们从任意一个点开始,都可以遍历整个连通块)。
对于每个连通块,我们还是任意选一个点开始编号,经过一条边的时候将编号加上边权,和上面一样,第二次经过同一个点的时候,它第二次的编号减去第一次的编号就是环的大小。
接下来处理情况 2。我们发现,上面的建图方式已经很好地处理了这种情况(走反边的时候权值
-1,刚好可以得到两条链长度的差值),因此不需要再另外进行处理。(最后别忘了题目要求面具最少要有三种)
#include <algorithm> #include <cstdio> #include <cstring> #include <vector> #define INF 1e9 using namespace std; struct edge { int v, w; bool operator<(const edge& a) const { return v < a.v || (v == a.v && w < a.w); } }; vector<edge> e[100005]; int dis[100005], vis[100005], ans, res, cnt, maxv, minv; int gcd(int x, int y) { return y == 0 ? x : gcd(y, x % y); } void dfs(int u, int d) { if (dis[u]) { ans = gcd(ans, abs(d - dis[u])); return; } dis[u] = d, vis[u] = 1; maxv = max(maxv, dis[u]); minv = min(minv, dis[u]); for (auto i : e[u]) dfs(i.v, d + i.w); } int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { int u, v; scanf("%d%d", &u, &v); e[u].push_back({v, 1}); e[v].push_back({u, -1}); } for (int i = 1; i <= n; i++) if (!vis[i]) { maxv = -INF, minv = INF; dfs(i, 1); res += maxv - minv + 1; } if (ans) { if (ans < 3) puts("-1 -1"); else for (int i = 3; i <= ans; i++) if (ans % i == 0) { printf("%d %d\n", ans, i); break; } } else { if (res < 3) puts("-1 -1"); else printf("%d 3\n", res); } return 0; }
- 1
信息
- ID
- 470
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者