传送门
题意:给一个二分图,一个完美匹配的权值等于完美匹配的边的权值的乘积,求所有完美匹配权值的和,保证至少有一个完美匹配。
眼里解释:第一行为T,T组数据,然后一行为n,表示有二分图每个集合有n个点,
然后下面有n行,每行四个数,例如第i行的四个数,v1,w1,v2,w2,那么就表示 i 与v1相连,权值为w1,i与v2相连,权值为w2。即左边集合就是i,(1<=i<=n),右边集合为输入的v1,v2.这样一来,我们发现左边集合每个点都有两个出度,右边的点每个点的入度大于等于1。那么我们可以先枚举右边集合的点,如果右边集合的点入度为1的话,那么该点(记作v)必须和与之相连的左边集合的点(记作u)进行匹配,那么与u相连的点的入度都要减去一,因为u被用了,与之相连的点不要再挂念他了,所以与u相连的点的入度都要减去一,那么如果发现入度减一以后度数变成1了,好,对该点再进行刚才操作。所以就是一个拓扑的过程。这些边的权值乘起来,(设乘积为common)重复利用就好了。
那么剩余的点怎么搞呢,不难发现剩余的点的左边集合每个点的出度还都是2,我们去掉的那些左边集合点,每个点出度是2,与之对应右边集合的点也去掉了2的倍数个,设左边去掉了k个点,那么右边的点的入度去掉了2*k个度数,回过头来看左边集合每个点有两个出度,一共有2*n个出度,那么左边集合一定有2*n个入度啊,2*n-2*k,左边剩下的点的度数和还是偶数啊!设剩下m个点,左边m个点每个点出度为2,右边m个点度数和为2*m,每个点度数大于等于2,所以每个点度数为2,左右两边的集合每个点度数都为偶数,那么剩下的图肯定是一个欧拉回路,例图:
6 点入度 为1,那么去点3->6,以及与3连的其他所有边,即3->5,剩余的图是一个欧拉回路,1->4->2->5->1。那么这个回路可以恰好肯定形成两个完美匹配,
一:1->4, 2->5
二:1->5, 2->4
我们要的是完美匹配的边的权值之积,设两个完美匹配的权值之积为x,y;x,y初始为1,x=x*val(1->4)*val(2->5), y=y*val(1->5)*val(2->4)。
最终结果ans= common*x+common*y=common*(x+y);然后ans%MOD;
#include <iostream>
#include <queue>
#include <stdio.h>
#include <string.h>
using namespace std;
typedef long long LL;
const int MAXM=1200100;
const int MAXN=600100;
const LL MOD=998244353;
struct Edge
{
int next,to;
LL w;
} edge[MAXM];
int head[MAXN],tot;
int in[MAXN];
int vis[MAXN];
LL common;
int vex[MAXN],cnt;
int quan[MAXN];
void init()
{
memset(head,-1,sizeof(head));
memset(in,0,sizeof(in));
memset(vis,0,sizeof(vis));
cnt=0;
tot=0;
common=1;
}
void add(int u,int v,LL w)
{
in[v]++;
edge[tot].to=v;
edge[tot].w=w;
edge[tot].next=head[u];
head[u]=tot++;
}
void solve1(int n)
{
queue<int>que;
for(int i=n/2; i<=n; i++)///枚举右边集合的点
{
if(in[i]==1)
que.push(i);
}
int u,v,v2;
while(!que.empty())
{
u=que.front();
que.pop();
vis[u]=1;
for(int i=head[u]; i!=-1; i=edge[i].next)
{
v=edge[i].to;
if(!vis[v])
{
vis[v]=1;
common=(common*edge[i].w)%MOD;
for(int j=head[v]; j!=-1; j=edge[j].next)
{
v2=edge[j].to;
in[v2]--;
if(in[v2]==1&&!vis[v2])
que.push(v2);
}
break;
}
}
}
for(int i=1; i<=n; i++)
if(!vis[i])
vex[cnt++]=i;
}
int find_nx(int u)
{
int v;
for(int i=head[u]; i!=-1; i=edge[i].next)
{
v=edge[i].to;
if(!vis[v])
return v;
}
return 0;
}
LL getw(int u,int v)
{
for(int i=head[u]; i!=-1; i=edge[i].next)
if(edge[i].to==v)
return edge[i].w;
return 1LL;
}
void solve2()
{
int u,v;
int top;
LL x,y;
LL ans=1;
for(int i=0; i<cnt; i++)
{
u=vex[i];
if(vis[u]==0)
{
top=0;
vis[u]=1;
quan[++top]=u;
for(v=find_nx(u); v ; v=find_nx(v))
{
quan[++top]=v;
vis[v]=1;
}
x=y=1;
quan[top+1]=quan[1];
for(int j=1; j<=top; j+=2)
x=(x*getw(quan[j],quan[j+1])%MOD)%MOD;
for(int j=2; j<=top; j+=2)
y=(y*getw(quan[j],quan[j+1])%MOD)%MOD;
ans=ans*(x+y)%MOD;
}
}
printf("%lld\n",common*ans%MOD);
}
int main()
{
int n,T;
int v1,v2;
LL w1,w2;
cin>>T;
while(T--)
{
scanf("%d",&n);
init();
for(int u=1; u<=n; u++)
{
scanf("%d%lld%d%lld",&v1,&w1,&v2,&w2);
add(u,v1+n,w1);
add(v1+n,u,w1);
add(u,v2+n,w2);
add(v2+n,u,w2);
}
solve1(n*2);
solve2();
}
return 0;
}
/**
3
2
2 1 1 4
1 4 2 3
*/