「PKUWC2018」隨機演算法
阿新 • • 發佈:2019-01-02
題面
題解
簡單狀壓$dp$
我們考慮把一個點放進獨立集中,
所有與它相鄰的點和還沒有考慮的點在後面任意一個位置都對答案沒有影響
其中我們認為考慮了一個點,當且僅當這個點在獨立集中或者與獨立集中的點聯通
那麼列舉下一個考慮的點,這個點一定可以加入獨立集
設$f[i][S]$表示獨立集大小為$i$,已經考慮的點集為$S$的方案數
記憶化搜尋即可
程式碼
#pragma GCC target ("popcnt") #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #define RG register #define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout); #define clear(x, y) memset(x, y, sizeof(x)) inline int read() { int data = 0, w = 1; char ch = getchar(); while(ch != '-' && (!isdigit(ch))) ch = getchar(); if(ch == '-') w = -1, ch = getchar(); while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar(); return data * w; } #define pcnt __builtin_popcount const int Mod(998244353), maxn(30), LIM(1 << 20); inline int Add(int x, int y) { return (x + y) % Mod; } int n, m, a[maxn], Ans, f[maxn][LIM], P[maxn][maxn], S; bool vis[maxn][LIM]; int dp(int x, int y) { if(vis[x][y]) return f[x][y]; vis[x][y] = true; if(y == S) return (f[x][y] = (x == Ans)); for(RG int i = 0; i < n; i++) if(!(y & (1 << i))) { int newy = y | (1 << i) | a[i]; f[x][y] = Add(f[x][y], 1ll * dp(x + 1, newy) * P[n - pcnt(y) - 1][pcnt(a[i] & (S ^ y))] % Mod); } return f[x][y]; } int fastpow(int x, int y) { int ans = 1; while(y) { if(y & 1) ans = 1ll * ans * x % Mod; x = 1ll * x * x % Mod, y >>= 1; } return ans; } int main() { #ifndef ONLINE_JUDGE file(cpp); #endif S = (1 << (n = read())) - 1, m = read(); for(RG int i = 0; i <= n; i++) { P[i][0] = 1; for(RG int j = 1; j <= i; j++) P[i][j] = 1ll * P[i][j - 1] * (i - j + 1) % Mod; } for(RG int i = 1, x, y; i <= m; i++) { x = read() - 1, y = read() - 1; a[x] |= (1 << y), a[y] |= (1 << x); } Ans = 0; int s, f; for(RG int i = 1; i <= S; i++) { f = 1, s = pcnt(i); if(s < Ans) continue; for(RG int j = 0; j < n; j++) if((i & (1 << j)) && (i & a[j])) { f = 0; break; } if(f) Ans = s; } s = 1; for(RG int i = 1; i <= n; i++) s = 1ll * s * i % Mod; printf("%lld\n", 1ll * dp(0, 0) * fastpow(s, Mod - 2) % Mod); return 0; }