「PKUWC2018」随机算法
题面
题解
简单状压$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;
}