HDU 6073 Multi-University Training Contest - Team 4 1007 :Matching In Multiplication:拓扑排序+思维

题意:给出一个二部图,U、V分别是二部图的两个点集,其中,U中每个点会有两条边连到V中两个不同的点。完美匹配定义为:所有点都成功匹配。完美匹配的权值定义为:匹配的边集所有边长度的乘积。图的权值定义为:所有不同方案的完美匹配的权值的和。给出的图满足一定存在完美匹配。

题解:这个题开始拿到不知道如何入手,只知道U中每个点出度都是2,并且U、V都有n个点,那么很显然V、U都有2n的度。考虑匹配:U中每个点有两个度,那么每个点有两种备选方案。V中所有点的度是2n,但是每个点的度可以各不相同,比如说某个点 y 的度是1,设这个边是 z ,那么很显然他必须匹配这一条边,那么这条边对应U中的点假设是x,那么 x 一定要用 z 这个边去匹配 y 这个点。于是x的另外一条边 t 就没用了,我们把他去掉,那么假设 t 连接了 x 和 p ,那么p的度就少了1,如果说此时p的度变成了1,那么好巧,p也只剩下一个救命稻草了,以此类推,我们从度为1的点出发,先找到了一系列 “必须这样匹配“ 的点和边。假设通过上述方法确定了 k 个点的唯一匹配方案,那么现在 U中剩余了n-k个点,V也是,而且U中有2*(n-k)的度,V也是,而且,V中入度为1的点已经全部被摘除,剩下的点入度必然>=2,(n-k)个点,2(n-k)的总度,每个点度>=2,那么说明每个点的度必为2。这就构成了环,而且在一个环中,只有两种方案A,B。答案=ANS1*(A1+B1)*(A2+B2)*……把括号展开的每一项就是所有的完美匹配的权值。

Code:

#include<bits/stdc++.h>
using namespace std;
const int TZY = 998244353;
const int MAX = 300050;
bool vis[MAX*2];
int rd[MAX*2];
long long ans;
struct Edge{
	int des,length;
	Edge(int des_,int length_):des(des_),length(length_){}
};
vector<Edge> E[MAX*2];
int n;
long long tempAns,tempAns1;
int que[MAX*5];
int l,r;
int cot[MAX*2];
void init(){
	for(int i=0;i<MAX*2;i++){
		E[i].clear();
	}
	memset(rd,0,sizeof(rd));
	memset(vis,false,sizeof(vis));
	memset(cot,0,sizeof(cot));
	l=0;
	r=0;
	ans = 1;
}
void input(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		int v1,w1,v2,w2;
		scanf("%d %d %d %d",&v1,&w1,&v2,&w2);
		E[i].push_back(Edge(v1+n,w1));
		E[v1+n].push_back(Edge(i,w1));
		E[i].push_back(Edge(v2+n,w2));
		E[v2+n].push_back(Edge(i,w2));
		rd[i]+=2;
		rd[v1+n]++;
		rd[v2+n]++;
	}
	for (int i=1;i<=2*n;i++){
		if (rd[i]==1){
			r++;
			que[r] = i;
		}
	}
}

void dfs(int node,int fa,int root,int flag){
	vis[node] = true;
	for (Edge temp :E[node]){
		int v = temp.des;
		int length = temp.length;
		if (v==fa){
			continue;
		}
		if (!vis[v]||v==root){
			if (!flag){
				tempAns1*=length;
				tempAns1%=TZY;
			}else{
				tempAns*=length;
				tempAns%=TZY;
			}
		}
		if (!vis[v]){
			dfs(v,node,root,flag^1);
		}
	}

}
void work(){
	while (l<r){
		l++;
		int q = que[l];
		vis[q] = true;
		if (rd[q]==0){
			continue;
		}
		for (Edge temp :E[q]){
			int v = temp.des;
			int length = temp.length;
			rd[v]--;
			if (rd[v]==1&&!vis[v]){
				r++;
				que[r] = v;
				if (q>n){
					ans*=length;
					ans%=TZY;
				}
			}
		}
	}
	for (int i=1;i<=n;i++){
		if (!vis[i]){
			tempAns=1LL;
			tempAns1=1LL;
			dfs(i,0,i,0);
			ans*=(tempAns1+tempAns);
			ans%=TZY;
		}
	}
	printf("%I64d\n",ans);
}
int main(){
	freopen("1007.in","r",stdin);
	int t;
	scanf("%d",&t);
	while (t--){
		init();
		input();
		work();
	}
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值