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 ",++C,ans); } return 0; }