hdu 6321 Problem C. Dynamic Graph Matching【2018 Multi-University Training Contest 3 】【状压DP】

【题意】:

t组样例,n个点(保证是偶数),m次操作,每次操作告诉你两个点,给这两个点加一条边或者减一条边,减边的话保证减边之前至少有一条边。

每次操作之后输出 整个图中 匹配大小为 j 的数量  \left ( j = \left \{ 1,2,3,...,n/2 \right \} \right ) 。两条重边被认为是不一样的。

【题解】:

n <= 10,每个点有被占用和不被占用两种状态,所以一共有2^10种状态。

如果这次增加了 a b这两个点的一条边,那么就从 0 - 1023中找到,a和b在二进制相应位数下是0的状态,然后更新这个数字a和b相应位数下变成1的状态。

即用 num[a]表示1<<(a-1),dp[ i+num[a]+num[b] ]直接加上当前dp[ i ]的答案,并取模。

如果这次删除了a b这两个点的一条边,那么就从0 - 1023中找到,a和b在二进制相应位数下是1的状态,从a和b相应位数为0的状态来更新当前答案。

即dp[ i ] 减去 dp[ i -num[a] -num[b] ] 并且取模。

在加和减的操作的同时,对ans[ ]数组进行更新,ans[1]表示匹配大小为1的答案,以此类推。

【代码】:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod=1e9+7;
ll dp[1025];
ll num[15];//预处理,num[i]表示 1<<i
ll ans[15];
void add(int a,int b)
{
    for(int i=0;i<1024;i++){
        if((i&num[a])==0&&(i&num[b])==0){
            int cnt=0;
            for(int j=1;j<=i;j<<=1){
                if((i&j)>0) cnt++;
            }
            if(cnt%2==0){
                dp[i+num[a]+num[b]]=(dp[i+num[a]+num[b]]+dp[i])%mod;
                ans[cnt/2+1]=(ans[cnt/2+1]+dp[i])%mod;
            }
        }
    }
}
void sub(int a,int b)
{
    for(int i=0;i<1024;i++){
        if((i&num[a])>0&&(i&num[b])>0){
            int cnt=0;
            for(int j=1;j<=i;j<<=1){
                if((i&j)>0) cnt++;
            }
            if(cnt%2==0){
                dp[i]=(dp[i]-dp[i-num[a]-num[b]]+mod)%mod;
                ans[cnt/2]=(ans[cnt/2]-dp[i-num[a]-num[b]]+mod)%mod;
            }
        }
    }
}
int main(void)
{
    int caz,n;
    scanf("%d",&caz);
    for(int i=0;i<11;i++){
        num[i]=1<<i;
    }
    while(caz--){
        int n,m;
        memset(dp,0,sizeof(dp));
        memset(ans,0,sizeof(ans));
        dp[0]=1;
        scanf("%d%d",&n,&m);
        char ch[10];
        int a,b;
        for(int i=0;i<m;i++){
            scanf("%s%d%d",ch,&a,&b);

            if(ch[0]=='+'){
                add(a-1,b-1);
            }
            else{
                sub(a-1,b-1);
            }
            for(int i=1;i<=n/2;i++){
                printf("%lld",ans[i]);
                if(i==n/2) printf("\n");
                else printf(" ");
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/gymgym1212/article/details/81290959