HDU 6321 Dynamic Graph Matching(状压 dp)

题目链接:Dynamic Graph Matching

题意

定义一种图的匹配:在图上选择任意条边,在被选择的边中任意两条边之间没有公共点,则这些边就是图上的一种匹配。
在一个 n n 个节点的图上,最初这张图上的边数为 0,有 m m 次操作,每次操作为以下两种中的一种:

  1. + u v:在节点 u u 和节点 v 之间添加一条边;

    •  u v −   u   v :删掉一条在节点 u u v 之间的边,在删边之前保证节点 u,v u , v 之间至少存在一条边。
    • 在每次操作之后,对于每一个 k (k[1,n2]) k   ( k ∈ [ 1 , n 2 ] ) ,输出图上边数为 k k 的匹配的数量,两个节点之间的多条边视为不同的边。

输入

第一行为一个整数 T (1T10),接下去有 T T 组数据,每组数据第一行为两个整数 n,m (2n10,nmod2=0,1m30000),接下去 m m 行每行由一个字符 ch (ch{+,}) 和两个数字 u,v (1u,vn) u , v   ( 1 ≤ u , v ≤ n ) 组成,表示一次操作。

输出

对于每一次操作,输出 n2 n 2 个整数,分别表示当 k[1,n2] k ∈ [ 1 , n 2 ] 时,图上边数为 k k 的匹配数量对 109+7 取模的结果。

样例

输入
1
4 8
+ 1 2
+ 3 4
+ 1 3
+ 2 4
- 1 2
- 3 4
+ 1 2
+ 3 4
输出
1 0
2 1
3 1
4 2
3 1
2 1
3 1
4 2
题解

我们用 2n 2 n 个整数对应图上点的选择方案,整数的第 i i 个二进制位为 0 表示不选择第 i i 个点,否则表示选择第 i 个点,则一个数字表示一种选择点的状态。用 dp[i] d p [ i ] 记录某种匹配的边覆盖的点的状态为 i i 的情况下,匹配的数量,则 i 1 1 的个数必须为偶数,否则匹配数量就为 0
每当节点 u u v 之间的边的数量 +1 + 1 1 − 1 ,就会影响所有选点状态同时包含 u,v u , v 两点的 dp d p 值,如果 i i 的第 u v v 位都为 1,则 dp[i]+=d×dp[i(1<<v)(1<<u)] d p [ i ] + = d × d p [ i − ( 1 << v ) − ( 1 << u ) ] ,当 ch c h + d=1 d = 1 ,否则 d=1 d = − 1
初始状态 dp[0]=1 d p [ 0 ] = 1 ,用 ans[i] a n s [ i ] 记录在匹配边的数量为 i i 时的总方案数,设 biti 为数字 i i 的二进制位中 1 的数量,则 ans[j]=2ni=0dp[i] a n s [ j ] = ∑ i = 0 2 n d p [ i ] ,其中 j=biti2 j = b i t i 2

过题代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <algorithm>
#include <functional>
#include <iomanip>
#include <unordered_set>
#include <unordered_map>
using namespace std;

#define LL long long
const int maxn = 1000 + 100;
const int MOD = 1000000000 + 7;
int T, n, m, u, v, d;
char ch[2];
int dp[maxn], ans[maxn], bit[maxn];

int add(int a, int b) {
    int ret = a + b;
    if(ret < 0) {
        return ret + MOD;
    }
    if(ret >= MOD) {
        return ret - MOD;
    }
    return ret;
}

void solve(int u, int v, int d) {
    int tmp = (1 << u) | (1 << v);
    for(int i = 1; i < (1 << n); ++i) {
        if((bit[i] & 1) == 1) {
            continue;
        }
        if((i & tmp) == tmp) {
            dp[i] = add(dp[i], d * dp[i ^ tmp]);
            ans[bit[i] >> 1] = add(ans[bit[i] >> 1], d * dp[i ^ tmp]);
        }
    }
}

int main() {
    #ifdef LOCAL
        freopen("test.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    #endif // LOCAL
    ios::sync_with_stdio(true);

    for(int i = 0; i < maxn; ++i) {
        bit[i] = 0;
        for(int j = 0; j < 10; ++j) {
            if(((i >> j) & 1) == 1) {
                ++bit[i];
            }
        }
    }
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        memset(dp, 0, sizeof(int) * (1 << n));
        memset(ans + 1, 0, sizeof(int) * (n >> 1));
        dp[0] = 1;
        for(int i = 0; i < m; ++i) {
            scanf("%s%d%d", ch, &u, &v);
            d = ch[0] == '+'? 1: -1;
            solve(u - 1, v - 1, d);
            for(int j = 1; j <= (n >> 1); ++j) {
                if(j != 1) {
                    printf(" ");
                }
                printf("%d", ans[j]);
            }
            printf("\n");
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值