首页 > 技术文章 > 2018宁夏邀请赛K题Vertex Covers(高维前缀和 状压 折半

wzgg 2019-09-04 10:27 原文

https://vjudge.net/problem/Gym-102222K

题意:给定N点M边的无向图,每个点有点权。  点覆盖表示某个点集S{}覆盖了所有的边,其贡献是S中点权之积。 现在让你求所有满足条件的点集贡献之和。N<36,保证无重边,自环。

思路:点覆盖选谁不选谁肯定状压,N有36再来个折半,然后想办法合并两边。可以枚举左边那堆点的状态,对于左边没选中的那些点,若他连接的其他左边点都被选中了(否则该状态舍去),

求出他连接的右边那些点,即是右边的必选点,那么右边就可以选该必选点集或其超集,先把右边每个状态预处理即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
int a[maxn],sum[maxn],ltl[40],ltr[40],rtr[40],Mod;
void MOD(int &x){ if(x>=Mod) x-=Mod;}

int main(){
    int T,N,M,u,v,C=0;
    cin>>T;
    while(T--){
        cin>>N>>M>>Mod;
        rep(i,0,N-1) cin>>a[i];
        rep(i,0,N-1) ltl[i]=ltr[i]=rtr[i]=0;
        int L=(N+1)/2,R=N-L,ans=0;
        rep(i,1,M){
            cin>>u>>v;
            u--,v--;
            if(u>v) swap(u,v);
            if(u<L){
                if(v<L) ltl[u] |= (1<<v);
                else ltr[u] |= (1<<(v-L));
            }
            else rtr[u] |= (1<<(v-L));
        }
        rep(i,0,(1<<R)-1){
            int res = 1;
            rep(j,0,R-1){
                if(i&(1<<j)) res = 1LL*res*a[j+L]%Mod;
                else{
                    res = res*((rtr[j+L]|i)==i);
                    if(!res) break;
                }
            }
            sum[i] = res;
        }
        rep(i,0,R-1){
            rep(j,0,(1<<R)-1){
                if(!(j&(1<<i))) MOD(sum[j]+=sum[j|(1<<i)]);
            }
        }
        rep(i,0,(1<<L)-1){
            int res=1,need=0;
            rep(j,0,L-1){
                if(i&(1<<j)) res = 1LL*res*a[j]%Mod;
                else{
                    res = res*((ltl[j]|i)==i),need|=ltr[j];
                    if(!res) break;
                }
            }
            MOD(ans+=1LL*res*sum[need]%Mod);
        }
        printf("Case #%d: %d\n",++C,ans);
    }
    return 0;
}

 

推荐阅读