2018 Multi-University Training Contest 3 C(Dynamic Graph Matching)

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

猜你喜欢

转载自blog.csdn.net/snayf/article/details/81320590