题解 洛谷 P5492 【[PKUWC2018]随机算法】
考虑到随机排列来加入点等效每次随机一个点,符合独立集的限制就加入当前点集,不符合就不加入。
\(n\) 很小,考虑状压 \(DP\),设 \(f_S\) 为得到点集 \(S\) 内的点的最大独立集的概率,\(siz_S\) 为点集 \(S\) 内的点的最大独立集的大小。
\(DP\) 时枚举点集 \(S\) 中最后加入的一个点,将与该点相连的点都删去,得到点集 \(T\),通过 \(T\) 来转移。
先处理出 \(siz\):
\[siz_S = \max \left \{ siz_T +1\right \}
\]
然后 \(f\) 通过 \(siz\) 的限制转移即可:
\[f_S = \frac{1}{|S|} \sum_{siz_S=siz_T + 1} f_T
\]
还要除 \(|S|\) 的原因是点集 \(S\) 中最后加入的一个点是等概率的。
复杂度为 \(O(n2^n)\)。
\(code:\)
#include<bits/stdc++.h>
#define maxn 25
#define maxs 1050010
#define p 998244353
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,all;
int e[maxn],cnt[maxs];
ll inv[maxn],siz[maxs],f[maxs];
void init()
{
inv[1]=1;
for(int i=2;i<=n;++i) inv[i]=(p-p/i)*inv[p%i]%p;
for(int i=1;i<=all;++i) cnt[i]=cnt[i-lowbit(i)]+1;
}
int main()
{
read(n),read(m),all=(1<<n)-1,init();
for(int i=1;i<=n;++i) e[i]=1<<(i-1),siz[1<<(i-1)]=1;
for(int i=1;i<=m;++i)
{
int x,y;
read(x),read(y);
e[x]|=1<<(y-1),e[y]|=1<<(x-1);
}
for(int s=1;s<=all;++s)
for(int i=1;i<=n;++i)
if(s&(1<<(i-1)))
siz[s]=max(siz[s],siz[s&(~e[i])]+1);
f[0]=1;
for(int s=1;s<=all;++s)
{
for(int i=1;i<=n;++i)
if(s&(1<<(i-1)))
if(siz[s]==siz[s&(~e[i])]+1)
f[s]=(f[s]+f[s&(~e[i])])%p;
f[s]=f[s]*inv[cnt[s]]%p;
}
printf("%lld",f[all]);
return 0;
}