HDU 6321 2018HDU多校赛 第三场 Dynamic Graph Matching(状态压缩dp)

Problem C. Dynamic Graph Matching

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 566    Accepted Submission(s): 211

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<vn.

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

大致题意:一开始图是空的,总共有m个操作,每次可以添加或者删除一条边,每个操作结束后,输出这个图里面匹配数为1,2,...,n/2的方案数。

看到n的范围是10,然后操作数是3W,就应该直接想到这是一个状压dp的……一开始还往组合数学,容斥原理方面去想……还好最后做出来了。

首先,我们考虑加入一条边之后会产生多少贡献。显然是加入这条边之前,所有不包含这条边以及这两个点的匹配的数目。我们用dp[status][i]表示取的点的状态为status,然后构成i个匹配方案数。当加入边(x,y)之后,dp[status|(1<<x)|(1<<x)][i+1]也即在状态status的基础上加上这两个点之后匹配数多一的方案,会增加dp[status][i]这么多。对应的ans[i+1]也会增加这么多。然而实际上,可以减少一维,因为一个status对应一个i,status中1的个数除以2就是匹配数,可以用函数__builtin_popcount(status) O(1)确定1的个数。对于删边也是类似,只不过是反过来而已。由此我们可以写出转移方程:

                                 \large dp[status|(1<<x)|(1<<y)]+=dp[status]

                                 \large dp[status]-=dp[status\oplus (1<<x)\oplus(1<<y)]

当然了,第一个式子要满足status中没有选中x和y,即status&((1<<x)|(1<<y))==0。第二个式子中要满足status中含有x和y,即status&((1<<x)|(1<<y))==(1<<x)|(1<<y)。具体见代码:

#include<bits/stdc++.h>
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define mod 1000000007
#define LL long long


using namespace std;

int n,m,dp[1100],ans[10];

int main()
{
    int T;
    IO; cin>>T;

    while(T--)
    {
        cin>>n>>m;
        int nn=n/2;
        memset(dp,0,sizeof(dp));
        memset(ans,0,sizeof(ans));
        dp[0]=1;int lim=1<<n;
        for(int i=1;i<=m;i++)
        {
            char ch[2];
            int x,y,num;
            cin>>ch>>x>>y;
            x--,y--;
            num=(1<<x)|(1<<y);
            if (ch[0]=='+')
                for(int j=0;j<lim;j++)
                {
                    if (j&num) continue;
                    int nxt=j|num,t=__builtin_popcount(nxt)/2;
                    dp[nxt]+=dp[j]; if (dp[nxt]>=mod) dp[nxt]-=mod;
                    ans[t]+=dp[j]; if (ans[t]>=mod) ans[t]-=mod;
                }
            else
                for(int j=num;j<lim;j++)
                {
                    if ((j&num)!=num) continue;
                    int nxt=j^num,d=-dp[nxt]+mod;
                    dp[j]=(dp[j]+d)%mod;
                    int t=__builtin_popcount(j)/2;
                    ans[t]=(ans[t]+d)%mod;
                }
            cout<<ans[1];
            for(int j=2;j<=nn;j++)
                cout<<' '<<ans[j];
            cout<<endl;
        }
    }
    return 0;
}

 

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/81296583