題目鏈接
題目大意
給定一個二分圖,集合U和V各有n個點(n
分析
拿到這道題一開始就先從二分圖匹配的算法去思考,但沒想出來OTL。。。
其實可以換個角度思考。
如果一個點的度數爲1的話,那麼它的匹配方案肯定是固定的,因此我們可以先通過拓撲排序去掉集合V中度數爲1的點,對V中度數爲1的點都在U中找一個點與之匹配,也將其刪除。那麼這些對答案ans的貢獻爲ans*common。 (common爲這些唯一確定邊權的乘積)
對於拓撲排序後剩下的點,由於集合U中點的度數都爲2,集合V中點的度數都≥2,所以集合V的每個點的度數肯定也都爲2。由於不存在奇點,所以必是歐拉回路。所以剩下的圖由若干個環組成。
而對於每一個環,它的完美匹配只有兩種情況,即都間隔取邊,設第一種情況的邊權乘積爲
綜上,
附官方題解:
代碼
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
typedef long long int LL;
const int MAXN=900010;
const int MAXM=1200010;
const int MOD=998244353;
struct Edge
{
int to,next;
LL w;
}e[MAXM];
int n,edgenum,head[MAXN],ind[MAXN],cnt,node[MAXN],loop[MAXN];
LL common;
bool vis[MAXN];
void Add_Edge(int u,int v,LL w)
{
ind[v]++;
e[++edgenum].to=v;
e[edgenum].w=w;
e[edgenum].next=head[u];
head[u]=edgenum;
}
void TopoSort()///拓撲去掉所有能唯一確定的配對,找出剩下成環的結點
{
queue<int> Q;
for (int i=1;i<=2*n;i++)
if (ind[i]==1)
Q.push(i);
while (!Q.empty())
{
int u=Q.front();
Q.pop();
vis[u]=true;
for (int t=head[u];t!=-1;t=e[t].next)
{
int v=e[t].to;
if (!vis[v])
{
LL w=e[t].w;
common*=w;
common%=MOD;
vis[v]=true;///
for (int tt=head[v];tt!=-1;tt=e[tt].next)
{
int k=e[tt].to;
ind[k]--;
if (!vis[k]&&ind[k]==1)
Q.push(k);
}
//break;
}
}
}
cnt=0;
for (int i=1;i<=2*n;i++)
if (!vis[i])
node[++cnt]=i;///node[]記錄成環的結點
}
LL dist(int u,int v)///算u到v的距離
{
for (int t=head[u];t!=-1;t=e[t].next)
if (e[t].to==v)
return e[t].w;
return 0;
}
int next(int u)///找環上與u相鄰的一個點
{
for (int t=head[u];t!=-1;t=e[t].next)
{
int v=e[t].to;
if (!vis[v])
return v;
}
return 0;
}
void Solve()
{
LL ans=1;
for (int i=1;i<=cnt;i++)
{
int u=node[i];///u爲某一個環的第一個結點
if (!vis[u])
{
int len=0;
vis[u]=true;
loop[++len]=u;///loop[]記錄一個環
for (int j=next(u);j;j=next(j))///DFS遍歷環
{
loop[++len]=j;
vis[j]=true;
}
loop[len+1]=loop[1];
LL x=1,y=1;
/*間隔取邊的兩種方案*/
for (int j=1;j<=len;j+=2)
x=x*dist(loop[j],loop[j+1])%MOD;
for (int j=2;j<=len;j+=2)
y=y*dist(loop[j],loop[j+1])%MOD;
ans=ans*(x+y)%MOD;
}
}
printf("%I64d\n",ans*common%MOD);
}
int main()
{
int T,v1,v2,i;
LL w1,w2;
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
edgenum=0;
memset(head,-1,sizeof(head));
memset(ind,0,sizeof(ind));
for (i=1;i<=n;i++)
{
scanf("%d%lld%d%lld",&v1,&w1,&v2,&w2);
Add_Edge(i,v1+n,w1);///注意二分圖的建圖
Add_Edge(v1+n,i,w1);
Add_Edge(i,v2+n,w2);
Add_Edge(v2+n,i,w2);
}
common=1;
memset(vis,false,sizeof(vis));
TopoSort();
Solve();
}
return 0;
}
總結:
1.這種用拓撲解決匹配問題的思想值得記錄
2.注意拓撲排序的BFS寫法
3.注意二分圖的建圖(V中結點的編號爲i+n)
4.注意long long型與格式對應符的對應(因爲這個不知道WA了多少次。。。)