Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 1298 Accepted Submission(s): 543
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
题意:
有一个n点的无向图,初始时没有边,有m次操作,每次可以增加或删除一条边(注意,允许重边的存在)
要求每一次操作之后,输出匹配数分别为1,2,3...n/2的方案数。
匹配是指:选中一些边,使得两两没有公共点,即每两个点可以通过边匹配起来。
思路:
官方给出的题解是状压dp做,毕竟n<=10。下面是官方题解
设 f[i][S] 表示前 i 次操作之后,S 集合的点已经匹配的方案数。 //S表示的是状态,表示该节点选与不选
对于加边操作,显然 f[i][S] = f[i−1][S] +f[i−1][S−u−v]。
从网上又看到一位大佬使用滚动数组的dp的,更好理解。具体实现可以看代码。
代码:
const int maxn=2005;
const ll mod=1e9+7;
ll n,m;
ll dp[2][maxn];
ll ans[15];
int cnt[maxn];
void init()
{
for(int i=0;i<maxn;i++)
{
cnt[i]=__builtin_popcount(i);//找出i这个数转为二进制有多少个1
}
}
int main()
{
int T;
int now,pre;
char op[5];
init();
scanf("%d",&T);
while(T--)
{
scanf("%lld%lld",&n,&m);
int up=1<<n;//所有状态
pre=0;
now=1;
memset(dp,0,sizeof(dp));
dp[0][0]=1;
while(m--)
{
int u,v;
scanf("%s %d %d",op,&u,&v);
u--;v--;
int temp=((1<<u)|(1<<v));//u这个位置和v这个位置选的状态
for(int i=0;i<up;i++) dp[now][i]=0;
if(op[0]=='+')
{
for(int i=0;i<up;i++)
{
dp[now][i]=dp[pre][i];
}
for(int i=0;i<up;i++)
{
if((i&temp)==0)//i这个状态并没有用到u和v点
{
int aim=(temp|i);//加边
dp[now][aim]=(dp[now][aim]+dp[pre][i])%mod;
}
}
}
else
{
for(int i=0;i<up;i++)
{
dp[now][i]=dp[pre][i];
}
for(int i=0;i<up;i++)
{
if((i&temp)==0)
{
int aim=(temp|i);
dp[now][aim]=(dp[now][aim]-dp[pre][i]+mod)%mod;
}
}
}
memset(ans,0,sizeof(ans));
for(int i=0;i<up;i++)
{
ans[cnt[i]]=(ans[cnt[i]]+dp[now][i])%mod;
}
for(int i=2;i<=n;i+=2)
{
if(i!=2) printf(" ");
printf("%lld",ans[i]);
}
printf("\n");
now=1-now;
pre=1-pre;
}
}
}