1 条题解

  • 0
    @ 2025-8-24 21:25:32

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 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。我们考虑倒推:建图的时候不仅连一条 uvu \to v,边权为 11 的边之外,同时连一条 vuv \to u,边权为 1-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
    上传者