HDU 6321 Problem C. 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): 984    Accepted Submission(s): 393

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.+ a b将a、b之间加一条无向边

2.- a b将a、b之间删除一条无向边(保证合法,不会删不存在的边)

然后每一次操作后,让你输出n/2个答案

第1个答案为,集合元素为1条边的集合的个数

第2个答案为,集合元素为2条边的集合的个数(集合里面不允许任意两条边有交点)

第3个答案为,集合元素为2条边的集合的个数(集合里面不允许任意两条边有交点)

....

解析:

官方题解

设f[i][S]表示前i次操作之后,S集合的点已经匹配的方案数。

对于加边操作,显然f[i][S]=f[i−1][S]+f[i−1][S−u−v]。i这一维可以省略,从大到小遍历S,f[S]+=f[S−u−v]

对于删边操作,注意到加边操作的顺序不影响结果,可以假设第i−1次操作是加入要删除的边。将加边操作的更新倒过来,得到:从小到大遍历S,f[S]−=f[S−u−v]。

时间复杂度O(m2^n)。

官方的题解我在看了之后,不知道答案怎么输出,所以自己又开了一维来保存答案。

dp[i][j]表示在i这种情况的子图下,集合大小为j的集合的个数

加边的操作就是 dp[i][j]+=dp[i-k][j-1] 

因为加边的时候,我们已经默认新加进去的边在集合里面了,所以集合的大小变成了j-1,然后我们再在i除去u,v这两点的子图找

集合大小为j-1的集合的个数就可以了

删边同理 dp[i][j]-=dp[i-k][j-1]

最后输出答案的时候,只要输出完整的图的答案就可以了dp[(1<<n)-1][1..n/2]

我这个复杂度来算的话是O((n/2)*m*2^n),理论上应该没问题的

不过我这个好像有点卡常了,dp数组用longlong 存在每一次初始化的时候会T的,

所以得用int来存dp才可以

#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <queue>
#include <algorithm>
#define lch root<<1
#define rch (root<<1)|1
using namespace std;
typedef long long ll;

const int MAXN = (1<<10);
const ll MOD = 1e9+7;

int dp[MAXN+10][6];

int main()
{
	int t;
	scanf("%d",&t);
	int n,m;
	while(t--)
	{
		scanf("%d%d",&n,&m);
		char c;
		int u,v;
		int big=(1<<n)-1;
		int am=n/2;
		for(int i=0;i<=big;i++)
		{
			dp[i][0]=1;    //这里是为了在加边后,集合大小为1的答案能够+1
			for(int j=1;j<=am;j++)
				dp[i][j]=0;
		}
		for(int _=1;_<=m;_++)
		{
			getchar();
			scanf("%c%d%d",&c,&u,&v);
			u--;
			v--;
			int k=(1<<u)|(1<<v);
			if(c=='+')
			{
				for(int i=big;i>=0;i--)
					if((k&i)==k)
					{
						for(int j=1;j<=am;j++)
							dp[i][j]=(1ll*dp[i][j]+1ll*dp[i-k][j-1])%MOD;
					}
			}
			else
			{
				for(int i=big;i>=0;i--)
					if((k&i)==k)
					{
						for(int j=1;j<=am;j++)
							dp[i][j]=(1ll*dp[i][j]-1ll*dp[i-k][j-1]+MOD)%MOD;
					}
			}
			for(int i=1;i<=am;i++)
			{
				if(i==1)
					printf("%d",dp[big][i]);
				else
					printf(" %d",dp[big][i]);
			}
			printf("\n");
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/81303994