2017多校4-7 Matching In Multiplication
图论,思维
题意
给你一个二分图,左右各n个点,左边每个点都连出两条边。保证至少有一个完美匹配,对于一个完美匹配,价值是边权之积,要求所有完美匹配的价值和。
思路
首先如果一个点的度数为1,那么它的匹配方案是固定的,继而我们可以去掉这一对点。通过拓扑我们可以不断去掉所有度数为1的点。
那么剩下的图中左右各有m个点,每个点度数都不小于2,且左边每个点度数都是2,而右侧总度数是2m,因此右侧只能是每个点度数都是2。这说明这个图每个连通块是个环,在环上间隔着取即可,一共两种方案。
时间复杂度O(n)。
代码
#include<bits\stdc++.h>
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
const int MAXN=600007;
const int mod=998244353;
int indeg[MAXN];
struct Edge
{
int to;int co;bool fl;int ne;
}e[2*MAXN];
int edge=0;
int head[MAXN];
void addedge(int u, int v, int c)
{
e[edge].to=v, e[edge].co=c, e[edge].fl=0;
e[edge].ne=head[u];head[u]=edge;
edge++;
}
int del[MAXN];
queue<int> que;
LL ans1=1, ans2=1;
void dfs(int now, int fl)
{
del[now]=1;
for(int i=head[now];~i;i=e[i].ne)
{
int to=e[i].to;int co=e[i].co;
if(e[i].fl) continue;
e[i].fl=1, e[i^1].fl=1;
if(fl) ans1=ans1*co%mod;
else ans2=ans2*co%mod;
dfs(to, fl^1);
}
}
int main()
{
int T;scanf("%d", &T);
while(T--)
{
while(!que.empty()) que.pop();
M(indeg, 0);M(del, 0);M(head, -1);
edge=0;
int n;scanf("%d", &n);
for(int i=1;i<=n;i++)
{
int a, b, c, d;scanf("%d%d%d%d", &a, &b, &c, &d);
addedge(i, a+n, b);
addedge(a+n, i, b);
indeg[i]++, indeg[a+n]++;
addedge(i, c+n, d);
addedge(c+n, i, b);
indeg[i]++, indeg[c+n]++;
}
//LL tmp=1;
for(int i=n+1;i<=2*n;i++)
{
if(indeg[i]==1)
{
que.push(i);
}
}
LL ans=1;
while(!que.empty())
{
int now=que.front();que.pop();
del[now]=1;
for(int i=head[now];~i;i=e[i].ne)
{
if(e[i].fl) continue;
e[i].fl=1;e[i^1].fl=1;
int to=e[i].to;
del[to]=1;
ans=(ans*e[i].co)%mod;
for(int j=head[to];~j;j=e[j].ne)
{
if(e[j].fl) continue;
e[j].fl=1;e[j^1].fl=1;
indeg[e[j].to]--;
if(indeg[e[j].to]==1) que.push(e[j].to);
}
}
}
for(int i=1;i<=2*n;i++)
{
if(!del[i])
{
ans1=ans2=1;
dfs(i, 0);
ans=ans*((ans1+ans2)%mod)%mod;
}
}
printf("%lld\n", ans);
}
}