考虑到随机排列来加入点等效每次随机一个点,符合独立集的限制就加入当前点集,不符合就不加入。
(n) 很小,考虑状压 (DP),设 (f_S) 为得到点集 (S) 内的点的最大独立集的概率,(siz_S) 为点集 (S) 内的点的最大独立集的大小。
(DP) 时枚举点集 (S) 中最后加入的一个点,将与该点相连的点都删去,得到点集 (T),通过 (T) 来转移。
先处理出 (siz):
[siz_S = max left { siz_T +1
ight }
]
然后 (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;
}