HDU 6073 Matching In Multiplication —— 2017 Multi-University Training 4

Matching In Multiplication

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 1389    Accepted Submission(s): 423

Problem Description
In the mathematical discipline of graph theory, a bipartite graph is a graph whose vertices can be divided into two disjoint sets  U and V (that is, U 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 U is 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(1T15), denoting the number of test cases.

In each test case, there is an integer n(1n300000) in the first line, denoting the size of U. The vertex in U and V are labeled by 1,2,...,n.

For the next n lines, each line contains 4 integers vi,1,wi,1,vi,2,wi,2(1vi,jn,1wi,j109), 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
 
题意:左右两边各有n个顶点的集合,分别为U,V,从U的每个顶点会有连出两条带权边与V的顶点相连。题目要求我们选取若干条边,使两个集合的所有点得到覆盖,并且定义这种情况为“完美匹配“,”完美匹配“的值为所选取的边权值求积,最后输出所有完美匹配的值之和。
 
思路:首先对于V的所有点来说,入度为1的点一定在U集合有唯一的点与之相连,在每种”完美匹配“里它们的配对是固定的,于是就可以扫一遍所有唯一对应的点对,求出它们的边权之积 left。
在排除以上情况的点之后,U集合点的出度均为2,假如V集合存在入度大于2的点,必存在另外有个点入度小于2。然而这是不可能的,否则就和上一种情况矛盾。所以剩余V集合里的点入度均为2,剩余的边构成了环。接着间隔取点,每个环的”完美匹配“结果分两种s[0], s[1],求出所有s[0]+s[1], 与left相乘取模得到结果。(补题时没好好理解题意,把相乘写成相加了(/TДT)/)
 
AC代码:
  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<vector>
  5 #include<fstream>
  6 #include<queue>
  7 using namespace std;
  8 typedef long long LL;
  9 const int MAXN=6e5+10;
 10 const int N=3e5;
 11 const LL MOD=998244353;
 12 struct Edge{
 13     int to;
 14     LL w;
 15 };
 16 vector<Edge> edge[MAXN];
 17 int deg[MAXN]; 
 18 LL s[2];
 19 void dfs(int node,int pos, bool ext){
 20     //cout<<node<<' '<<s[0]<<' '<<s[1]<<' '<<deg[node]<<' '<<ext<<endl;
 21     for(int i=0;i<edge[node].size();i++){
 22         int p=edge[node][i].to;
 23         if(!deg[p])
 24             continue;
 25             
 26         if(deg[p]==2)
 27         {
 28             deg[node]--;
 29             deg[p]--;
 30             s[pos]=s[pos]*edge[node][i].w%MOD;
 31             dfs(p, pos^1, ext);    
 32             break;
 33         }
 34         else if(deg[node]==1&&deg[p]==1)
 35         {
 36             if(ext==false)
 37                 ext=true;
 38             else{
 39                 deg[node]--;
 40                 deg[p]--;
 41                 s[pos]=s[pos]*edge[node][i].w%MOD;
 42                 return;
 43             }
 44         }
 45     }
 46 }
 47 
 48 int main()
 49 {
 50     //ifstream cin("ylq.txt");
 51     int T;
 52     cin>>T;
 53     int n,v1,v2;
 54     LL w1,w2;
 55     Edge e1,e2;
 56     while(T--)
 57     {
 58         memset(deg, 0, sizeof(deg));
 59         //cin>>n;
 60         scanf("%d", &n);
 61         for(int i=1;i<=N+n;i++){
 62             edge[i].clear();
 63         }
 64         for(int i=1;i<=n;i++){
 65             //cin>>v1>>w1>>v2>>w2;
 66             scanf("%d %lld %d %lld", &v1, &w1, &v2, &w2);
 67             deg[v1+N]++;
 68             deg[v2+N]++;
 69             deg[i]+=2;
 70             
 71             e1.to=v1+N;e1.w=w1;
 72             e2.to=v2+N;e2.w=w2;
 73             edge[i].push_back(e1);
 74             edge[i].push_back(e2);
 75             
 76             e1.to=i;e2.to=i;
 77             edge[v1+N].push_back(e1);
 78             edge[v2+N].push_back(e2);
 79         }
 80         
 81         LL left=1;
 82         queue<int> q;
 83         for(int i=N;i<=n+N;i++)
 84             if(deg[i]==1)
 85                 q.push(i);
 86                 
 87         int m;
 88         while(!q.empty())
 89         {
 90             int p, pp;
 91             m=q.front(); q.pop();
 92             deg[m]=0;
 93             for(int i=0;i<edge[m].size();i++){
 94                 p=edge[m][i].to;
 95                 if(!deg[p])
 96                     continue;
 97                 else
 98                 {
 99                     deg[p]=0;
100                     left=left*edge[m][i].w%MOD;
101                     for(int k=0;k<edge[p].size();k++){
102                         pp=edge[p][k].to;
103                         if(deg[pp]==0) continue;
104                         
105                         deg[pp]--;
106                         if(deg[pp]==1)
107                             q.push(pp);
108                     }
109                 }
110             }
111              
112         }
113         //cout<<'*'<<left<<'*'<<endl;
114         LL ans=left;
115         for(int i=1;i<=n;i++){
116             if(!deg[i])
117                 continue;
118             s[0]=s[1]=1;
119             dfs(i, 1, 0);
120             ans=ans*(s[0]+s[1])%MOD;
121         }
122         
123         printf("%lld\n", ans);
124     }
125 }

我的代码里用入度出度判断是否走到重复点,看了好多人都是用vis判断的,感觉都差不多。。。。

 

转载于:https://www.cnblogs.com/MasterSpark/p/7295808.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值