hdu 6073 Matching In Multiplication(无向图+拓扑排序+dfs找环)

16 篇文章 1 订阅
9 篇文章 0 订阅

Matching In Multiplication
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 1666 Accepted Submission(s): 506

Problem Description
In the mathematical discipline of graph theory, a bipartite graph is a graph whose vertices can be divided into two disjoint sets Uand V(that is, and V are each independent sets) such that every edge connects a vertex in U to one in V. Vertex sets U and V are usually called the parts of the graph. Equivalently, a bipartite graph is a graph that does not contain any odd-length cycles. A matching in a graph is a set of edges without common vertices. A perfect matching is a matching that each vertice is covered by an edge in the set.

Little Q misunderstands the definition of bipartite graph, he thinks the size of Uis equal to the size of V, and for each vertex p in U, there are exactly two edges from p
. Based on such weighted graph, he defines the weight of a perfect matching as the product of all the edges’ weight, and the weight of a graph is the sum of all the perfect matchings’ weight.
Please write a program to compute the weight of a weighted ”bipartite graph” made by Little Q.

Input
The first line of the input contains an integer T(1≤T≤15), denoting the number of test cases.

In each test case, there is an integer n(1≤n≤300000)in the first line, denoting the size of U. The vertex in Uand Vare labeled by 1,2,…,n.

For the next nlines, each line contains 4integers vi,1,wi,1,vi,2,wi,2(1≤vi,j≤n,1≤wi,≤10^9)
, denoting there is an edge between Ui and Vvi,1, weighted wi,1, and there is another edge between Ui and Vvi,2, weighted wi,2.

It is guaranteed that each graph has at least one perfect matchings, and there are at most one edge between every pair of vertex.

Output
For each test case, print a single line containing an integer, denoting the weight of the given graph. Since the answer may be very large, please print the answer modulo
998244353
.

Sample Input
1
2
2 1 1 4
1 4 2 3

Sample Output
16

题意:
求二分图所有完美匹配中边权值乘积的和(一个完美匹配中所有边权值乘起来,在将不同情况的完美匹配的边权值乘积相加)

解析:
题目保证至少存在一个完美匹配,U中所有顶点的度数都等于2,V中所有顶点的度数都大于等于1,那么对于V中度数为1的点,它们对所有完美匹配的权值的贡献common是不变的,因此,我们可以先处理V中度数为1的点(用拓扑排序)。

假设V中度数为1的点有k个,那么在U中我就需要用k个点来与V中的k个度数为1的点匹配,然后U和V中都剩下m个点,m=(n-k)。此时我们可以知道U和V中所有点的度数都大于等于2了。(U中会存在度数为1的点吗?不存在的。U中顶点的度数只有我们在处理V中顶点的时候才会改变,那么当我们在处理V中度数为1的顶点N的时候,说明U中只有一个顶点M跟N相连,此时的处理只涉及N和M,所以U中别的顶点的度数还是跟初始一样。)

继续,U中m个顶点的度数都是2,那么V中m个顶点的总度数就是2*m,又因为此时V中m个顶点的度数都大于等于2,那么我们可以知道V中m个顶点的度数都是2。此时我们可以发现,U∪V中的所有顶点的度数都是偶数,当完美匹配完成后,此时图中必然存在欧拉回路,也就是环

对于一个环中,如O0->O1->O2->O3->O0,我们可以理解为(1)O0与O1匹配,O2和O3匹配,(2)O1和O2匹配,O3和O0匹配,也就是说,对于一个已经完成的匹配,它只存在两种方案。那么我们就可以在一个环中,通过枚举起点O0和O1来找出当前环的两种匹配权值X1,Y1。由于图中可能存在多个环,我们需要枚举U中m个点来找出所有的环(用DFS),一个环对于结果的贡献值是common*(X1+Y1);那么P个环就是common*(X1+Y1)(X2+Y2)….*(XP+YP)=ans
,ans就是最后的结果了。
解析原文:
http://blog.csdn.net/mr__kid/article/details/76684660

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#define MAXN 600120
#define MOD 998244353   
using namespace std;
typedef long long int ll;

typedef struct node
{
    int v;
    long long int w;
}node;

int n;
vector<node> map[MAXN];
int length[MAXN],visit[MAXN];
ll circle[2];
ll ans;
int flag,s;

ll toposort()   //拓扑排序
{
    ll common=1;
    queue<int> mq;

    for(int i=1;i<=n;i++)   //初始化节点的度数
        length[i]=map[i].size();
    for(int i=n+1;i<=2*n;i++)
    {
        length[i]=map[i].size();  //初始化节点的度数
        if(map[i].size()==1)    //寻找右边节点度数为1的点
        {
            length[i]=0;
            mq.push(i);
            visit[i]=1;
        }
    }

    while(mq.size())
    {
        int tmp=mq.front();
        mq.pop();

        for(int i=0;i<map[tmp].size();i++)    //根据tmp删除与tmp相连的边
        {
            int ver=map[tmp][i].v;
            if(length[ver]>=1)
            length[ver]--;

            if(length[ver]==1&&visit[ver]==0)   //若删完后,节点度数为1,则将该点加入
            {                                       
                if(tmp>n) common=(common*map[tmp][i].w)%MOD;   //二分图匹配只有一半的边时匹配边
                mq.push(ver);
                visit[ver]=1;
            }
        }
    }
    return common;


}

void dfs(ll x,ll u,ll ty)   //x当前节点,u上一个节点,ty第ty中匹配情况
{
    visit[x]=1;
    for(int i=0;i<map[x].size();i++)
    {
        int ver=map[x][i].v;
        if(ver==s&&ver!=u) circle[ty]=(circle[ty]*map[x][i].w)%MOD;  //判断是否成环
        if(visit[ver]) continue;

        circle[ty]=(circle[ty]*map[x][i].w)%MOD;
        dfs(ver,x,ty^1);
    }


}

void solve()
{
    ll common=1;
    memset(visit,0,sizeof(visit));
    common=toposort();

    ll res=1;
    for(s=1;s<=2*n;s++)
    {
        if(visit[s]==1) continue;
        circle[0]=circle[1]=1;
        flag=0;
        dfs(s,0,0);

        res=(res*(circle[0]+circle[1]))%MOD;
    }

    ans=(res*common)%MOD;
    printf("%lld\n",ans);

}


int main()
{
    int t;
    int a;
    ll b;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld",&n);
        for(int i=1;i<=2*n;i++) map[i].clear();

        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<2;j++)
            {
                node tmp;
                scanf("%d%lld",&a,&b);
                tmp.v=a+n;
                tmp.w=b;
                map[i].push_back(tmp);

                tmp.v=i;
                map[a+n].push_back(tmp);

            }
        }
        solve();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值