P6914 [ICPC2015 WF]Tours 题解
考虑环套环:
容易发现,环套环一共是三环,任意一条边一定属于且仅属于两个环,且所属环编号相同的边一定可以构成一条链。现在恰好有三类链,假设三个环编号分别为 \(h1,h2,h3\),三条链分别为 \(l1,l2,l3\),假设现在钦定有三种颜色,数量为 \(x,y,z\),并给每个环(或链)给定一个二元组 \(ele=(ele_1,ele_2)\),两个元素分别表示 \(x-y,y-z\),根据题意可列出如下方程:
\[ele_{h1}=ele_{h2}=ele_{h3}=(0,0)
\]
由于两链组成一个环得:
\[\left\{\begin{array}{l}
ele_{l1}+ele_{l2}=ele_{h1}\\
ele_{l2}+ele_{l3}=ele_{h2}\\
ele_{l1}+ele_{l3}=ele_{h3}
\end{array}\right.\]
联立得:
\[ele_{l1}=ele_{l2}=ele_{l3}=(0,0)
\]
由此可以推广到:所属环编号完全相同的边组成的链,每种颜色数量相等。这样问题就变成了对于所有这样的链,每种颜色数量都相同。
然后开始找这样的链。考虑使用 \(\text{tarjan}\):观察图发现,桥边一定不在环上,不做考虑,对于非桥边,暴力枚举,把当前边删除,然后再跑一遍 \(\text{tarjan}\),所有新增的桥边都是和当前所删的的边所属环编号完全相同的边,这些边共同组成一条符合上述条件的链。
求出所有链长的 \(\gcd\),这个值的所有约数都是符合条件的答案。
代码:
// Problem: P6914 [ICPC2015 WF]Tours
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6914
// Memory Limit: 500 MB
// Time Limit: 1875 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define int long long
#define maxy(x, y) x = max(x, y)
#define miny(x, y) x = min(x, y)
#define lowbit(x) (x & (-x))
#define fu(i, l, r) for (int i = l; i <= r; ++i)
#define fd(i, r, l) for (int i = r; i >= l; --i)
#define mem(x, y) memset(x, y, sizeof(x))
using namespace std;
namespace Std {
void read(int &x) {
x = 0;
int y = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') y = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c & 15);
c = getchar();
}
x *= y;
}
void write(int x) {
if (x < 0) {
putchar('-');
write(-x);
return;
}
if (x > 9) write(x / 10);
putchar((x % 10) | 48);
}
int n, m, in[20001], to[20001], nxt[20001], head[5001],
cnt = 1, dfn[5001], stack[5001], top = 0, tot = 0, low[5001], belong[5001];
bool mark[20010];
vector<int> ans, vec;
void add(int x, int y) {
in[++cnt] = x;
to[cnt] = y;
nxt[cnt] = head[x];
head[x] = cnt;
}
void tarjan(int x, int rt) {
dfn[x] = low[x] = ++tot;
stack[++top] = x;
for (int i = head[x]; i; i = nxt[i]) {
if (mark[i]) continue;
if ((~rt) && (i == (rt ^ 1))) {
continue;
}
if (!dfn[to[i]]) {
tarjan(to[i], i);
low[x] = min(low[x], low[to[i]]);
} else {
if (i) {
low[x] = min(low[x], dfn[to[i]]);
}
}
}
if (low[x] == dfn[x]) {
cnt++;
int a = 0;
while (x != a) {
a = stack[top--];
belong[a] = cnt;
}
}
}
int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }
int main() {
read(n);
read(m);
for (int i = 1; i <= m; i++) {
int x, y;
read(x);
read(y);
add(x, y);
add(y, x);
}
cnt = 0;
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
tarjan(i, -1);
}
}
int le = 0;
fu(i, 1, m) if (belong[in[i * 2]] != belong[to[i * 2]]) mark[i * 2 + 1] =
mark[i * 2] = 1;
fu(i, 1, m) {
if (belong[in[i * 2]] == belong[to[i * 2]] && (!mark[i * 2])) {
mark[i * 2] = mark[i * 2 + 1] = 1;
mem(dfn, 0);
vec.push_back(i);
tot = cnt = top = 0;
fu(j, 1, n) {
if (!dfn[j]) tarjan(j, -1);
}
int tle = 1;
fu(j, 1, m) {
if ((!mark[j * 2]) && belong[in[j * 2]] != belong[to[j * 2]]) {
tle++;
mark[j * 2] = mark[j * 2 + 1] = 1;
vec.push_back(j);
}
}
for (auto j : vec) mark[j * 2] = mark[j * 2 + 1] = 0;
le = gcd(le, tle);
}
}
for (int i = 1; i * i <= le; ++i) {
if (!(le % i)) {
ans.push_back(i);
if (le / i != i) ans.push_back(le / i);
}
}
sort(ans.begin(), ans.end());
for (auto i : ans) printf("%lld ", i);
return 0;
}
} // namespace Std
#undef int
int main() { return Std::main(); }
未经授权,禁止转载。