HDU - 6321 Problem C. Dynamic Graph Matching (状压dp)

题意:给定一个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;
}

猜你喜欢

转载自www.cnblogs.com/xiuwenli/p/9398342.html