题意:给定一个N个点的零图,M次操作,添加或删除一条边,每一次操作以后,打印用1,2,...N/2条边构成的匹配数。
分析:因为N的范围很小,所以可以把点的枚举状态用二进制表示集合。用一维数组dp[S]表示二进制集合为S的点集的匹配数。
每次加边操作,从大到小遍历集合,dp[S]+=dp[S-u-v];删边操作,从小到大遍历集合,dp[S]-=dp[S-u-v]。
预处理出每个1024之内每个数对应二进制含有1的个数,每次记录答案就将每个dp[S]加到ans[S对应的二进制个数]中。
#include<bits/stdc++.h> using namespace std; const int maxn =1030; const int mod = 1e9+7; typedef long long LL; void add(int &a,int b){a=a+b<mod?a+b:a+b-mod;} void del(int &a,int b){a=a-b<0?a-b+mod:a-b;} int dp[maxn],ans[15],cnt[maxn]; int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif int T,N,M,u,v; char op[5]; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); int tot=1<<N; for(int i=0;i<tot;++i){ dp[i]=0; cnt[i] = __builtin_popcount(i); } dp[0]=1; while(M--){ scanf("%s%d%d",op,&u,&v); memset(ans,0,sizeof(ans)); u--,v--; int S = (1<<u)|(1<<v); //取只包含u,v的集合 if(op[0]=='+'){ for(int t=tot-1;~t;--t) if(!(t&S)) add(dp[t^S],dp[t]); //加上原来不包含的u,v的集合的匹配数 } else{ for(int t=0;t<tot;++t) if(!(t&S)) del(dp[t^S],dp[t]); //减去原来不包含u,v的集合的匹配数 } for(int i=1;i<tot;++i) add(ans[cnt[i]],dp[i]); for(int i=2;i<=N;i+=2) printf("%d%c",ans[i],i<N?' ':'\n'); } } return 0; }