QOJ #14432. Cyclic Topsort 题解

Description

给定一个有向图,图中有 \(n\) 个顶点和 \(m\) 条边。任务是找到一个从 \(1\)\(n\) 的不同整数构成的最长序列 \(a\),使得以下条件成立:

  • 设序列 \(a\) 的长度为 \(l\)。那么对于所有整数 \(i \in [2, l]\),以及所有顶点 \(v\),如果存在一条边 \((v, a_i)\),则必定存在一个索引 \(j < i\),使得 \(a_j = v\)

\(n,m\leq 3\times 10^5\)

Solution

先不管 \(a_1\) 的特殊性跑一遍拓扑排序,则这样能够入队的点永远能够入队,可以将其删掉。

加上 \(a_1\) 后,等价于在现在跑完的图上再将 \(a_1\) 放到队列里继续跑拓扑排序,暴力枚举 \(a_1\) 可以得到一个 \(O(n^2)\) 的做法。

\(S_u\) 表示 \(a_1=u\) 时相比初始必选点的增量集合,那么就是求 \(\max|S_u|\)

有个结论是所有不同的 \(S_u\)\(S_v\) 一定包含或者不交。

证明

考虑钦定 \(|S_u|\leq |S_v|\),如果 \(S_u\)\(S_v\) 有交且互不包含,则一定找到两条不同的边 \((x_1,y),(x_2,y)\),使得 \(y\neq u,y\neq v\)\(x_1\)\(x_2\) 分别在 \(S_u\setminus S_v\)\(S_v\setminus S_u\) 中,且 \(y\) 同时在 \(S_u\)\(S_v\) 中,但是出现这种情况的话两边一定不能拓展到 \(y\),也就矛盾了。

根据这个结论,如果 \(v\in S_u\),则 \(S_v\subseteq S_u\),就不再需要计算 \(a_1=v\) 的答案了。所以可以随机打乱排列,按顺序枚举排列的每一个点,如果当前点没有被拓展到过,则将这个点加入队列并计算答案,再更新是否拓展过的标记。

这个做法的期望复杂度是 \(O((n+m)\log n)\) 的。证明考虑对于每个点 \(u\),计算其被拓展的期望次数。先按照集合的包含关系建树,把 \(u\) 的所有祖先 \(p_1,p_2,\ldots,p_t\),则 \(u\) 被拓展的次数是在排列中这些祖先节点中 \(dep\) 的前缀最小值数量,这个是经典结论,为 \(O(\log n)\),所以总复杂度就是 \(O((n+m)\log n)\)

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 3e5 + 5;

int n, m, cnt;
int deg[kMaxN], _deg[kMaxN];
bool vis[kMaxN];
std::vector<int> G[kMaxN];
std::mt19937 rnd(std::random_device{}());

int solve(int s) {
  std::queue<int> q;
  std::vector<int> vid;
  q.emplace(s);
  int ret = 0;
  for (; !q.empty();) {
    int u = q.front(); q.pop();
    vis[u] = 1, ++ret;
    for (auto v : G[u]) {
      if (v == s) continue;
      if (deg[v] == _deg[v]) vid.emplace_back(v);
      if (!--deg[v]) q.emplace(v);
    }
  }
  for (auto i : vid) deg[i] = _deg[i];
  return ret;
}

void dickdreamer() {
  std::cin >> n >> m;
  for (int i = 1; i <= m; ++i) {
    int u, v;
    std::cin >> u >> v;
    G[u].emplace_back(v);
    ++deg[v];
  }
  {
    std::queue<int> q;
    for (int i = 1; i <= n; ++i)
      if (!deg[i])
        q.emplace(i);
    for (; !q.empty();) {
      int u = q.front(); q.pop();
      vis[u] = 1, ++cnt;
      for (auto v : G[u]) {
        if (!--deg[v]) q.emplace(v);
      }
    }
    for (int i = 1; i <= n; ++i) _deg[i] = deg[i];
  }
  std::vector<int> id;
  for (int i = 1; i <= n; ++i) id.emplace_back(i);
  std::shuffle(id.begin(), id.end(), rnd);
  int ans = cnt;
  for (auto i : id) {
    if (!vis[i]) {
      ans = std::max(ans, cnt + solve(i));
    }
  }
  std::cout << ans << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}
posted @ 2025-10-15 16:31  下蛋爷  阅读(25)  评论(0)    收藏  举报