HDU6321 Dynamic Graph Matching(状态dp,类比于背包问题)

Problem Description

In the mathematical discipline of graph theory, a matching in a graph is a set of edges without common vertices.
You are given an undirected graph with n vertices, labeled by 1,2,...,n. Initially the graph has no edges.
There are 2 kinds of operations :
+ u v, add an edge (u,v) into the graph, multiple edges between same pair of vertices are allowed.
- u v, remove an edge (u,v), it is guaranteed that there are at least one such edge in the graph.
Your task is to compute the number of matchings with exactly k edges after each operation for k=1,2,3,...,n2. Note that multiple edges between same pair of vertices are considered different.

Input

The first line of the input contains an integer T(1≤T≤10), denoting the number of test cases.
In each test case, there are 2 integers n,m(2≤n≤10,nmod2=0,1≤m≤30000), denoting the number of vertices and operations.
For the next m lines, each line describes an operation, and it is guaranteed that 1≤u<v≤n.

Output

For each operation, print a single line containing n2 integers, denoting the answer for k=1,2,3,...,n2. Since the answer may be very large, please print the answer modulo 109+7.

Sample Input

 

1 4 8 + 1 2 + 3 4 + 1 3 + 2 4 - 1 2 - 3 4 + 1 2 + 3 4

Sample Output

 

1 0 2 1 3 1 4 2 3 1 2 1 3 1 4 2

Source

2018 Multi-University Training Contest 3

题意:给定一个 n 个点的无向图,m 次加边或者删边操作。
在每次操作后统计有多少个匹配包含 k = 1, 2, ...,
n/2 条边。

匹配的意思是,选择的边不含有公共点。

思路:因为n<=10,所以可以考虑状压dp。

dp[i][S]表示,前i次操作,在S的点集合可以选择的方案数量。

加边:dp[i][S]=dp[i-1][S]+dp[i-1][S-u-v];//等价于一次01背包

减边: dp[i][S] =dp[i][S]-dp[i][S-u-v];  减边的操作可能难以理解,其实它等价于一个只进行一次操作的完全背包,

根据类似于背包的滚动,我们可以将其优化为1维dp。

剩下的就是程序实现了:

最后的答案ans为ans[2],ans[4].....

分别表示2个点(1条边),4个点(2条边).......

#include<cstdio>
#include <bits/stdc++.h>
using namespace std;
const int N=1<<10,P=1000000007;
int T,n,m,all,i,x,y,S,f[N],cnt[N],ans[N];char op[9];
inline void add(int&a,int b){a=a+b<P?a+b:a+b-P;}
inline void sub(int&a,int b){a=a-b>=0?a-b:a-b+P;}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        all =1<<n;
        for(int i=0;i<all;i++)
        {
            cnt[i]=__builtin_popcount(i);//统计1的个数,最后ans需要用到
            f[i]=0;
        }
        f[0]=1;//初始化,没有任何点的时候,算1一种方案
        while(m--)
        {
            scanf("%s%d%d",op,&x,&y);
            x--,y--;
            int S=(1<<x)|(1<<y);//利用位运算保存当前两个点
            if(op[0]=='+')
            {
                for(int i=all-1;i>=0;i--)
                {
                    if((i&S)==0)//注意,只需要更新可以包含新加点的状态
                    {
                        add(f[i^S],f[i]);//dp递推式
                    }
                }
            }
            else
            {
                for(int i=0;i<all;i++)
                {
                    if((i&S)==0)
                    {
                        sub(f[i^S],f[i]);//将减边操作类比于加边操作
                    }
                }
            }
            for(int i=0;i<=n;i++) ans[i]=0;
            for(int i=1;i<all;i++)
            {
                add(ans[cnt[i]],f[i]);//递推求出答案
            }
            for(i=2;i<=n;i+=2)printf("%d%c",ans[i],i<n?' ':'\n');
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40774175/article/details/81293027