zoukankan      html  css  js  c++  java
  • LOJ2540 [PKUWC2018] 随机算法 【状压DP】

    题目分析:

    听说这题考场上能被$ O(4^n) $的暴力水过,难不成出题人是毕姥爷?

    首先思考一个显而易见的$ O(n^2*2^n) $的暴力DP。一般的DP都是考虑最近的加入了哪个点,然后删除后递归进行状压DP。由于这道题的题目询问方式是反过来的,处理方式也反过来。

    令$ f[n][S] $表示当前有$ S $这些点,期望这些点能够构成独立集大小为$ n $。正向的考虑选择了哪个点,并把与这个点有连边的所有点在集合内进行删除,令找到的新状态为$ f[n-1][P] $。我们把$ P $中的结点与$ S $中不在$ P $中的点进行标号拼接。写成语言就是$ f[n][S]+=f[n-1][P] * inom{|S|-1}{|P|} * |P|! $ 由于对于$ S $的每一个点都需要转移一遍,时间复杂度就变成了$ O(n^2*2^n) $。虽然跑得不快,但是由于冗余状态较多,考场上一定比例的人使用这个算法通过了这个题。

    现在来考虑把它优化到$ O(n*2^n) $。由于题目期望着你获得一个最大独立集,所以我们可以发现第一维是没有必要的。因为对于一个目标状态$ S $,我们如果知道$ S $对应的最大独立集的大小的话,那么我们必定是奔着这个大小而去的。现在我们用$ g[S] $来表示$ S $对应的最大独立集大小,那么这是一个普及组题目,枚举选点然后求max就行了。再对于$ f[S] $,求$ S $对应的最大独立集大小。首先记录$ g[S] $,然后找删除某个点后的集合变为了$ g[S]-1 $的就是我们想要的转移方案,同样采用带标号的拼接。因为我们没有了第一维的负担,所以时间复杂度骤降为了$ O(n*2^n) $.

    ps:我终于会用letax数学公式啦。

     

    代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int maxn = 25;
     5 const int mod = 998244353;
     6 
     7 int n,m;
     8 int connect[maxn];
     9 int f[(1<<20)+5],g[(1<<20)+5],sz[(1<<20)+5];
    10 int arr[(1<<20)+5],C[maxn][maxn],fac[25];
    11 
    12 int fast_pow(int now,int pw){
    13     if(pw == 1) return now;
    14     int z = fast_pow(now,pw/2);
    15     z = (1ll*z*z) % mod;
    16     if(pw & 1) z = (1ll*z*now)%mod;
    17     return z;
    18 }
    19 
    20 void read(){
    21     scanf("%d%d",&n,&m);
    22     for(int i=1;i<=m;i++){
    23     int u,v; scanf("%d%d",&u,&v);
    24     connect[u] |= (1<<v-1);
    25     connect[v] |= (1<<u-1);
    26     }
    27     for(int i=1;i<=n;i++) connect[i] |= (1<<i-1);
    28 }
    29 
    30 void init(){
    31     C[0][0] = 1;
    32     for(int i=1;i<=n;i++){
    33     C[i][0] = C[i][i] = 1;
    34     for(int j=1;j<i;j++) C[i][j] = (C[i-1][j-1]+C[i-1][j])%mod;
    35     }
    36     for(int i=1;i<(1<<n);i++){
    37     for(int j=0;j<n;j++) if((1<<j)&i) sz[i]++;
    38     }
    39     fac[0] = 1;
    40     for(int i=1;i<=n;i++) fac[i] = (1ll*fac[i-1]*i)%mod;
    41 }
    42 
    43 void dfs(int now){
    44     arr[now] = 1;
    45     for(int i=1;i<=n;i++){
    46     if(!((1<<i-1)&now)) continue;
    47     int p = now - (now&connect[i]);
    48     if(!arr[p]) dfs(p);
    49     g[now] = max(g[now],g[p]+1);
    50     }
    51 }
    52 
    53 void dfs2(int now){
    54     arr[now] = 1;
    55     for(int i=1;i<=n;i++){
    56     if(!((1<<i-1)&now)) continue;
    57     int kk = (now&connect[i]),p = now - kk;
    58     if(g[p] != g[now]-1) continue;
    59     if(!arr[p]) dfs2(p);
    60     f[now]+=((1ll*C[sz[now]-1][sz[p]]*fac[sz[kk]-1])%mod)*f[p]%mod;
    61     f[now] %= mod;
    62     }
    63 }
    64 
    65 void work(){
    66     init();
    67     g[0] = 0; arr[0] = 1; 
    68     for(int i=1;i<(1<<n);i++) if(!arr[i]) dfs(i);
    69     memset(arr,0,sizeof(arr));
    70     f[0] = 1; arr[0] = 1;
    71     dfs2((1<<n)-1);
    72     int ans = f[(1<<n)-1];
    73     ans = (1ll*fast_pow(fac[n],mod-2)*ans)%mod;
    74     printf("%d",ans);
    75 }
    76  
    77 int main(){
    78     read();
    79     work();
    80     return 0;
    81 }
  • 相关阅读:
    安装 macbook 双系统( OS X 和 Ubuntu )
    微信小程序初探
    [Javascript] Promise
    [AS/400] 基本概念
    [AS/400] Control Language(CL) 基本概念
    [AS/400] Control Language
    [github] 创建个人网页
    [tools] sublime 使用记录
    [Java] TreeMap
    exe转msi
  • 原文地址:https://www.cnblogs.com/Menhera/p/9064561.html
Copyright © 2011-2022 走看看