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