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');
}
}
}