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(); }
posted @ 2022-06-08 19:54  Wilson_Inversion  阅读(54)  评论(0编辑  收藏  举报