HDU-6073 Matching In Multiplication

来源:互联网 发布:大数据hadoop入门 编辑:程序博客网 时间:2024/06/04 20:51

题意:

"完美匹配"被认为是图中所有的点都被匹配的集合内的边所覆盖,一个"完美匹配"的权重是该匹配中所有边权的乘积,而一个"二分图"的权重是图中所有"完美匹配"的求和。

思路:

题目给的是二分图,但有一些限制,所以容易会想从二分图出发处理。实际上被骗了,由于左部的点度数都为2,所以去分析右部的点,发现当其度数等于1时,与其关联的边必选,所以需要以拓扑的思想去除所有这些度数为1的点及其相关联的左部的点及相应的边。然后剩余图就会是一个左部右部点数相同,且所有的点的度数都为2的图。

然后就去寻找图中的连通分量,寻找无向图的连通分量一般是去找点双连通分量,但这题不需要也不适用。事实上剩余图上所有连通的点就是一个连通分量,所以就又转化为了寻找连通块,并在寻找的过程中计算该连通块的方案值。寻找完连通块后,每一个连通块能贡献出两个值,再根据加法的结合率乘法的分解率,得到答案就等于每个连通块的两个值相加和后的乘积,再乘上必须选择的方案值(度数为1的点所造成的)即可。


代码:

#include <bits/stdc++.h>#define LL long longusing namespace std;const LL mod = 998244353;const int maxn = 6e5+5;struct node{LL w;int v, next;} edge[maxn*2];int no, head[maxn];int bad[maxn], deg[maxn];//bad记录删除的点,deg记录点的度数int vis[maxn];int T, N, NN;queue<int> q;vector<int> vtt;LL ans, must;inline void init(){no = 0;for(int i = 1; i <= NN; ++i)vis[i] = bad[i] = deg[i] = 0, head[i]=-1;}inline void add(int u, int v, LL w){edge[no].v = v; edge[no].w = w;edge[no].next = head[u]; head[u] = no++;}LL dfs(int cur, int father){vtt.push_back(cur);vis[cur] = 1;for(int k = head[cur], kk; k != -1; k = edge[k].next){int v = edge[k].v;if(v == father) continue;if(bad[v] || vis[v]) continue;vis[v] = 1; vtt.push_back(v);for(kk = head[v]; kk != -1; kk = edge[kk].next)if(!bad[edge[kk].v] && edge[kk].v != cur) break;return dfs(edge[kk].v, v)*edge[k].w%mod;}return 1;}int main(){//freopen("in.txt", "r", stdin);scanf("%d", &T);while(T--){scanf("%d", &N);NN = N<<1; init();for(int i = 1; i <= N; ++i){int v, w;scanf("%d %d", &v, &w);add(i, N+v, w); add(N+v, i, w);++deg[i], ++deg[N+v];scanf("%d %d", &v, &w);add(i, N+v, w); add(N+v, i, w);++deg[i], ++deg[N+v];}must = 1;//所有必选边造成的权重for(int i = 1; i <= N; ++i)if(deg[N+i] == 1) q.push(N+i);while(!q.empty()){int u = q.front(); q.pop();bad[u] = 1;for(int k = head[u]; k != -1; k = edge[k].next){int v = edge[k].v;if(bad[v]) continue;must = must*edge[k].w%mod;bad[v] = 1;for(int kk = head[v]; kk != -1; kk = edge[kk].next){if(bad[edge[kk].v]) continue;if(--deg[edge[kk].v] == 1)q.push(edge[kk].v);}}}ans = must;for(int i = 1; i <= N; ++i){//确定左部顶点的一条边便确定了整个连通块的所有边if(vis[i] || bad[i]) continue;LL ans1 = 0;for(int k = head[i], kk; k != -1; k = edge[k].next){int v = edge[k].v;vtt.clear();vis[v] = 1;for(kk = head[v]; kk != -1; kk = edge[kk].next)if(!bad[edge[kk].v] && edge[kk].v != i) break;ans1 = (ans1+dfs(edge[kk].v, v)*edge[k].w%mod)%mod;vis[v] = 0;for(int j = 0; j < vtt.size(); ++j)vis[vtt[j]] = 0;//清空当前连通块的标记,操作另一条边vtt.push_back(v);}for(int j = 0; j < vtt.size(); ++j)vis[vtt[j]] = 1;ans = ans*ans1%mod;}printf("%lld\n", ans);}return 0;}


继续加油~

原创粉丝点击