Security Camera [ABC220H]
阿新 • • 發佈:2021-10-22
https://atcoder.jp/contests/abc220/tasks/abc220_h
題解
考慮折半搜尋,將 \(n\) 個點分為大小為 \(\dfrac{n}{2}\) 的兩個集合 \(S, T\)
設 \(F1[s]\ (s\subseteq S)\) 表示如果選了 \(s\) 中的點安裝攝像頭,那麼被監視的邊的數量的奇偶性
設 \(F2[t]\ (t\subseteq T)\) 表示如果選了 \(t\) 中的點安裝攝像頭,那麼被監視的兩端都在 \(T\) 中的邊的數量的奇偶性
設 \(G[s]\ (s\subseteq S)\) 表示所有 (和 \(\{S\) \ \(s\}\) 中的點之間有奇數條邊的) 的 \(T\)
以上都可以在 \(O(n*2^{\frac{n}{2}})\) 的時間內計算
那麼一對 \(s,t\) 滿足題目條件當且僅當:
\[F1[s] \oplus F2[t] \oplus \ (\operatorname{popcount}(G[s]\& T)\&1) = 0 \]考慮如何計數
列舉 \(G[s]\& T\),考慮計算
\[H[P][0/1] = \sum\limits_{G[s]\& T = P} [F1[s]\oplus F2[t] = 0/1] \]\[Ans=\sum\limits_{P\subseteq T} H[P][\operatorname{popcount}(P)] \]發現上面第一個式子長得像個集合交卷積
列舉 \(v1=0/1, v2=0/1\),計算 \(C1[P]=\sum\limits_{G[s]=P} [F1[s]=v1], C2[P]=[F2[P]=v2]\),
那麼 \(H[P][v1\oplus v2] = \sum\limits_{s\& t = P} C1[s]*C2[t]\)
使用 \(FWT\) 在 \(O(n*2^{\frac{n}{2}})\) 的時間內計算
程式碼
作者:AK_DREAM 出處:http://www.cnblogs.com/ak-dream/ 本文版權歸作者和部落格園共有,歡迎轉載,但必須給出原文連結,並保留此段宣告,否則保留追究法律責任的權利。#include <bits/stdc++.h> #define N 50 #define M (1<<20)+5 #define pb push_back #define lb(x) (x&-x) using namespace std; typedef long long ll; int n, m, G[M], cnt[M]; bool F1[M], F2[M]; ll C1[M], C2[M]; vector<int> E[N]; inline int chk(int s, int x) { return (s>>x)&1; } void FWT(ll *F, int _n, int tp) { for (int i = 1; i < (1<<_n); i <<= 1) { for (int j = 0; j < (1<<_n); j += i+i) for (int k = 0; k < i; k++) { F[j+k] += tp*F[i+j+k]; } } } int main() { scanf("%d %d", &n, &m); for (int i = 1, u, v; i <= m; i++) { scanf("%d %d", &u, &v); E[u].pb(v); E[v].pb(u); } int t1 = n/2, t2 = n-t1; for (int s = 1; s < (1<<t1); s++) { int t = s^lb(s), x = log2(lb(s))+1; F1[s] = F1[t]; G[s] = G[t]; for (auto y : E[x]) { if ((y<=t1&&!chk(s,y-1))||y>t1) F1[s]^=1; if (y > t1) G[s] ^= (1<<(y-t1-1)); } } for (int s = 1; s < (1<<t2); s++) { int t = s^lb(s), x = log2(lb(s))+t1+1; F2[s] = F2[t]; cnt[s] = cnt[t]+1; for (auto y : E[x]) { if (y > t1 && !chk(s,y-t1-1)) F2[s]^=1; } } ll ans = 0; int mx = (1<<t1)-1; for (int v1 = 0; v1 <= 1; v1++) for (int v2 = 0; v2 <= 1; v2++) { memset(C1, 0, sizeof(C1)); memset(C2, 0, sizeof(C2)); for (int s = 0; s < (1<<t1); s++) if (F1[s] == v1) ++C1[G[mx^s]]; for (int s = 0; s < (1<<t2); s++) if (F2[s] == v2) ++C2[s]; FWT(C1, t2, 1); FWT(C2, t2, 1); for (int s = 0; s < (1<<t2); s++) C1[s] = C1[s]*C2[s]; FWT(C1, t2, -1); for (int s = 0; s < (1<<t2); s++) { if (!((cnt[s]&1)^v1^v2)) ans += C1[s]; } } printf("%lld\n", ans); return 0; }