1. 程式人生 > 其它 >LOJ #3165. 「CEOI2019」遊樂園

LOJ #3165. 「CEOI2019」遊樂園

LOJ #3165. 「CEOI2019」遊樂園

對於原圖上任意一種 \(\text{DAG}\) 的方案, 假設該方案翻轉了 \(x\) 條邊, 那麼把所有邊再翻轉一遍仍然是個 \(\text{DAG}\), 並且在原圖的基礎上翻轉了 \(m-x\) 條邊. 若 \(\text{DAG}\) 數量為 \(C\), 則答案 \(\text{Ans}=C\times \dfrac{m}{2}\). 故可以將原問題規約為求 \(\text{DAG}\) 數量之和.

發現數據範圍 \(1\le n\le 18\). 顯然是狀壓 \(\text{DP}\).

\(f_{S}\) 表示點集 \(S\)

構成的匯出子圖為 \(\text{DAG}\) 的方案數. 接下來考慮如何轉移 \(\text{DP}\).

一個樸素的想法是列舉每一個點, 算出點 \(u\) 加入點集合 \(S\) 時的貢獻. 由於 \(\text{DP}\) 的轉移是有順序的, 可以不妨設加入的點 \(u\) 在 當前 \(\text{DAG}\) 中的入度為 \(0\). 於是點 \(u\)\(f_{S\cup\{u\}}\) 的貢獻為 \(f_{S}\).

但是, 上述的轉移會重複. 例如, 在集合 \(S\) 中新增兩個互相獨立的點 \(u,v\), 則 \(f_{S\cup\{u,v\}}\) 算了兩遍 \(f_{S}\)

. 於是可以考慮運用容斥原理去重.

運用容斥原理, 轉移 \(\text{DP}\) 時列舉 \(S\) 的子集 \(T\), 並且滿足 \(T\) 為獨立集. 則 \(T\)\(S\) 的貢獻為 \((-1)^{|T|-1}f_{S\backslash T}\). 故有轉移方程

\[f_{S}=\sum_{T\subseteq S}(-1)^{|T|-1}[T是獨立集]f_{S\backslash T} \]

暴力列舉子集轉移即可. 預處理獨立集的複雜度為 \(\mathrm O(2^nn^2)\), 總時間複雜度為 \(\mathrm O(2^nn^2+3^n)\).

參考程式碼
#include <bits/stdc++.h>
using namespace std;
static constexpr int mod = 998244353;
static constexpr int Maxn = 18;
int n, m, nn;
bool e[Maxn][Maxn];
bool d[1 << Maxn];
int f[1 << Maxn];
bool parity[1 << Maxn];
int main(void) {
  scanf("%d%d", &n, &m);
  nn = (1 << n) - 1;
  for (int i = 0; i <= nn; ++i)
    parity[i] = __builtin_parity(i);
  memset(d, true, sizeof(d));
  for (int i = 0; i < m; ++i) {
    int u, v;
    scanf("%d%d", &u, &v);
    --u, --v;
    e[u][v] = e[v][u] = true;
  }
  for (int s = 0; s <= nn; ++s) {
    d[s] = true;
    for (int i = 0; i < n; ++i) for (int j = i + 1; j < n; ++j)
      if ((s >> i & 1) && (s >> j & 1) && e[i][j])
        d[s] = false;
  }
  f[0] = 1;
  for (int s = 1; s <= nn; ++s) {
    for (int t = s; t; t = (t - 1) & s) {
      if (d[t]) {
        f[s] += (parity[t] ? f[s ^ t] : -f[s ^ t]);
        if (f[s] < 0) f[s] += mod;
        if (f[s] >= mod) f[s] -= mod;
      }
    }
  }
  int ans = f[nn];
  ans = 1LL * ans * m % mod;
  ans = 1LL * ans * (mod + 1) / 2 % mod;
  printf("%d\n", ans);
  exit(EXIT_SUCCESS);
} // main