HDU_6073 Matching In Multiplication 【二分图&&拓扑排序&&DFS】

来源:互联网 发布:nginx 自建dns解析 编辑:程序博客网 时间:2024/06/07 10:51

题目链接

题目描述

如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确地说:把一个图的顶点划分为两个不相交集 U 和V ,使得每一条边都分别连接U、V中的顶点。如果存在这样的划分,则此图为一个二分图。二分图的一个等价定义是:不含有「含奇数条边的环」的图完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。但是小Q错误的理解了二分图的定义,以为二分图的两个顶点集u和v的大小相等,u中的每个顶点p都可以引出两条边,它定义了一个二分图的权重就是每个完美匹配的权重相加,每个完美匹配的权重是这个完美匹配中所有边的乘积。

解题思路

首先如果一个点的度数为1,那么它的匹配方案是固定的,继而我们可以去掉这一对点。通过拓扑我们可以不断去掉所有度数为1的点。那么剩下的图中左右各有m个点,每个点度数都不小于2,且左边每个点度数都是2,而右侧总度数是2m,因此右侧只能是每个点度数都是2。这说明这个图每个连通块是个环,在环上间隔着取即可,一共两种方案。

代码部分

#include<bits/stdc++.h>using namespace std;const int mod = 998244353;const int N = 300000 + 10;struct Edge {    int nxt, to, w;    bool mrk;} e[N*4];///构造领接链表的边结点int T, n, head[N*2], cnt, deg[N*2], used[N*2];///T表示测试数据的组数,n表示节点个数,head表示头结点,deg记录每个结点的度,used表示这个节点是否被匹配过long long part[2];///part表示最后剩的那两部分void addedge(int u, int v, int w) {///构造领接链表    e[++cnt].nxt = head[u];    e[cnt].to = v;    e[cnt].w = w;    e[cnt].mrk = 0;    head[u] = cnt;}void dfs(int cur, int idx) {///因为要隔着取所以我们可以用part[0]表示一种情况,part[1]表示另一种情况,然后每次改变idx的值    used[cur] = 1;///表示这个顶点已经找过了,不能再找,所以要标记一下    for(int i=head[cur];i;i=e[i].nxt)    {        if(e[i].mrk)    continue;///如果这个顶点已经被用了,就跳过        e[i].mrk = e[i^1].mrk = 1;///没有则标记,并把它对应的那个点也标记了        (part[idx] *= e[i].w) %= mod;///每个完美匹配的累乘        dfs(e[i].to, 1-idx);///递归寻找另一个完美匹配边    }}int main(){    scanf("%d", &T);    while(T-- && scanf("%d", &n)!=EOF)    {        memset(head, 0, sizeof(head));        memset(deg, 0, sizeof(deg));        memset(used, 0, sizeof(used));        cnt = 1;        for(int u=1, v, w;u<=n;u++)        for(int i=1;i<=2;i++)        {            scanf("%d %d", &v, &w);            addedge(u, v+n, w);            addedge(v+n, u, w);            deg[u]++,   deg[v+n]++;        }        long long ans = 1;        queue<int> que;        for(int v=n+1;v<=n+n;v++)            if(deg[v] == 1)                que.push(v);        while(!que.empty())///对所有度为1的点进行拓扑排序,并去掉度为1节点        {            int v = que.front();            que.pop();            used[v] = 1;///标记度为1的节点            for(int i=head[v];i;i=e[i].nxt)            {                if(e[i].mrk)    continue;                e[i].mrk = e[i^1].mrk = 1;                used[ e[i].to ] = 1;///因为该节点与度为1的节点相连,所以也要去掉                (ans *= e[i].w) %= mod;                for(int j=head[ e[i].to ];j;j=e[j].nxt)///将于这个点相连的所有边去掉,判断去掉便后,剩余节点的度是否为1,如果为1也要加入队列中                {                    e[j].mrk = e[j^1].mrk = 1;                    deg[e[j].to]--;                    if(deg[ e[j].to ] == 1) que.push(e[j].to);                }            }        }        //cout<<"ans: "<<ans<<endl;        for(int u=1;u<=n;u++)        {            if(used[u]) continue;            part[0] = part[1] = 1;            dfs(u, 0);///调用dfs计算剩余的完美匹配            (ans *= (part[0]+part[1]) % mod) %= mod;        }        printf("%lld\n", ans);    }}
阅读全文
2 0
原创粉丝点击