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<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
大致题意:一开始图是空的,总共有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的个数。对于删边也是类似,只不过是反过来而已。由此我们可以写出转移方程:
当然了,第一个式子要满足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;
}