编程语言
首页 > 编程语言> > 「PKUWC2018」随机算法

「PKUWC2018」随机算法

作者:互联网

题面

Solution

考虑状压DP。按照题意我们按顺序加点,如果该点不能加入独立集,那么这个点可以插在之后排列的某一个位置中。

我们记在独立集中的点的集合不在独立集中的点的个数,设 \(F(S,i)\) 表示当前独立集点的集合为 \(s\),还有 \(i\) 个点没有插入排列。

可以用高维前缀和预处理每个集合的相邻的点(包括集合本身的点。记为 \(g(S)\)),这样就可以 \(O(n^2 \times n^2)\) 的预处理每个点 \(u\) 当前集合加入独立集 \(s\) 后新的相邻的点,记为 \(h(u,S)\)。

我们有

\[ F(S,i-1) \leftarrow f(s,i)\times i \\ F(S\cup u,i+h(u,S)) \leftarrow F(S,i) (u\notin g(S)) (u\notin g(S)) \]

DP 的时间复杂度是 \(O(2^n \times n^2)\)。可以通过本题。当然这不是本题的最优复杂度。如果有人能够优化这个算法欢迎来联系我 /kel

code

#include<bits/stdc++.h>
#pragma GCC target("popcnt")
using namespace std;
const int N=21,Mod=998244353;
int g[1<<N],f[1<<N][N],t[N],ans,mx,n,m;
void upd(int& x, int y)
{
    x=(x+y>=Mod?x+y-Mod:x+y);
}
#define mul(x,y) (1ll*(x)*(y)%Mod)
inline int po(int x, int y=Mod-2)
{
    int r=1;
    while(y)
    {
        if(y&1) r=mul(r,x);
        x=mul(x,x), y>>=1;
    }
    return r;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<=m;++i)
    {
        scanf("%d%d",&u,&v),--u,--v;
        g[1<<u]|=(1<<u)|(1<<v);
        g[1<<v]|=(1<<u)|(1<<v);
    }
    for(int s=1;s<(1<<n);++s)
        for(int i=0;i<n;++i) if(s&(1<<i)) g[s]|=g[s^(1<<i)];
    f[0][0]=1;
    for(int s=0;s<(1<<n);++s)
    {
        for(int i=n;i;--i) upd(f[s][i-1],mul(f[s][i],i));
        memset(t,0,sizeof(t));
        for(int x=0;x<n;++x) if(!(g[s]&(1<<x)))
            t[x]=__builtin_popcount((g[1<<x]^(1<<x))&(~g[s]));
        for(int i=0;i<=n;++i) if(f[s][i])
            for(int x=0;x<n;++x) if(!(g[s]&(1<<x)))
                upd(f[s|(1<<x)][i+t[x]],f[s][i]);
    }
    for(int s=0;s<(1<<n);++s)
    {
        const int cnt=__builtin_popcount(s);
        if(cnt>mx&&f[s][0]) mx=cnt,ans=f[s][0];
        else if(cnt==mx) upd(ans,f[s][0]);
    }
    int fac=1; for(int i=1;i<=n;++i) fac=mul(fac,i);
    printf("%d",mul(ans,po(fac)));
}

标签:int,独立,times,算法,随机,集合,mul,PKUWC2018,Mod
来源: https://www.cnblogs.com/farway17/p/12007147.html