1. 程式人生 > 實用技巧 >題解 洛谷 P5492 【[PKUWC2018]隨機演算法】

題解 洛谷 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;
}