Loading

Solution - Codeforces 1394B Boboniu Walks on Graph

考虑先分析最后的图会长成什么样。
因为每个点都只会连出一条有向边,且最后还能走回自己。
所以可以知道的是图会有许多个环来组成,且每个环都无交。

但是这个判定条件明显不是很优秀,考虑继续转化。
考虑到对于一个有向环,每个点的出度和入度都需要为 \(1\)
那么出度为 \(1\) 题目本身就要求满足了,所以实际上只需要使得每个点的入度都为 \(1\) 就可以了。

那么继续转化,考虑反面,那就是要求不存在点的入度 \(> 1\)
(为什么不考虑入度 \(= 0\) 的?因为边数总共为 \(n\),有入度为 \(0\) 就一定有入度 \(> 1\) 的。)

那么就比较好做了,处理出一个点 \(v\) 对应的入边 \((u, v, w)\)\(u\) 处对应的出边的排名和 \(u\) 的入度,表示为二元组 \((rk_{(u, v, w)}, deg_u)\)
那么对于两条入边 \((u, v, w)\)\((u', v, w')\),就要求这两条边对应的二元组 \((rk_{(u, v, w)}, deg_u), (rk_{(u', v, w')}, deg_{u'})\) 不能同时被选中。

那么因为总共的二元组个数不超过 \(1 + 2 + \cdots + 9 = 45\) 个,可以直接二进制压缩。
\(f_u\) 表示选了 \(u\) 就不能选的二元组的集合,可以通过每一个点 \(v\) 的情况推出。

那么就可以直接爆搜,每次选出一个出度对应的二元组后判断是否会产生冲突即可。

时间复杂度 \(\mathcal{O}(nk\log k + k!)\)

#include<bits/stdc++.h>
using ll = long long;
constexpr int maxn = 2e5 + 10;
constexpr int pre[10] = {0, 0, 1, 3, 6, 10, 15, 21, 28, 36};
inline int id(int x, int y) {
   return x + pre[y];
}
int n, m, k;
std::vector<std::pair<int, int> > to[maxn];
int c[maxn][45];
ll f[maxn], g[45];
int ans;
void dfs(int d, ll mask) {
   if (d > k) {
      return ans++, void();
   }
   for (int i = 0; i < d; i++) {
      if (g[id(i, d)] & (mask | (1ll << id(i, d)))) continue;
      dfs(d + 1, mask | (1ll << id(i, d)));
   }
}
int main() {
   scanf("%d%d%d", &n, &m, &k);
   for (int x, y, z; m--; ) {
      scanf("%d%d%d", &x, &y, &z);
      to[x].emplace_back(z, y);
   }
   for (int i = 1; i <= n; i++) {
      std::sort(to[i].begin(), to[i].end());
      for (int j = 0; j < to[i].size(); j++) {
         int v = to[i][j].second, w = id(j, to[i].size());
         c[v][w]++;
         f[v] |= 1ll << w;
      }
   }
   for (int i = 1; i <= n; i++) {
      for (int w = 0; w < 45; w++) {
         if (f[i] >> w & 1ll) {
            g[w] |= f[i] ^ ((ll)(c[i][w] == 1) << w);
         }
      }
   }
   dfs(1, 0);
   printf("%d\n", ans);
   return 0;
}
posted @ 2024-11-12 21:54  rizynvu  阅读(9)  评论(0编辑  收藏  举报