题解:P10080 [GDKOI2024 提高组] 匹配
考场想出来了被卡常,交一发题解。
首先先找完美匹配,找不到就无解。
然后用网络流找到完美匹配后:
-
如果此完美匹配黑色边数恰好为偶数,直接输出即可。
-
如果此完美匹配黑色边数为奇数,我们注意到可以在残量网络上找一个黑边为单数的环来调整(网络流建反边的性质),使它变成黑色边数恰好为偶数。
这个找环可以把每个点拆成到达此点时黑边为奇数和偶数两个点,然后就变成了普通图找环。
这时候你直接整一个 \(O(nm)\) 的 bfs 找环,就会悲催地得到超时的好成绩。
其实可以用 dfs 避免找到重复点,时间复杂度 \(O(m)\)。
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<bitset>
#include<string>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1e3+10;
const int MAXM=1e6+10;
const int INF=0x3f3f3f3f;
const long long LINF=0x3f3f3f3f3f3f3f3f;
int n,m,s,t;
int idx=1;
int head[MAXN],nxt[MAXM],to[MAXM];
bool w[MAXM];
inline void add_edge(int x,int y,bool z){
idx++;
nxt[idx]=head[x];
head[x]=idx;
to[idx]=y;
w[idx]=z;
}
int dep[MAXN],cur[MAXN];
bool bfs(){
memset(dep,0,sizeof(dep));
queue <int> q;
q.push(s);
dep[s]=1;
while(!q.empty())
{
int x=q.front();
cur[x]=head[x];
q.pop();
for(int i=head[x];i;i=nxt[i])
{
if((!w[i])||dep[to[i]]){
continue;
}
dep[to[i]]=dep[x]+1;
q.push(to[i]);
}
}
if(dep[t]){
return true;
}
else{
return false;
}
}
int dfs(int x,int flow){
if(x==t){
return flow;
}
int res=0;
for(int i=cur[x];i;i=nxt[i])
{
cur[x]=i;
if(!w[i]){
continue;
}
if(dep[to[i]]!=dep[x]+1){
continue;
}
if(!dfs(to[i],w[i])){
dep[to[i]]=0;
continue;
}
res++;
flow--;
w[i]^=1;
w[i^1]^=1;
if(!flow){
return res;
}
}
return res;
}
int dinic(){//网络流找完美匹配
int flow=0;
while(bfs())
{
flow+=dfs(s,n);
}
return flow;
}
int e[MAXN];
int c[MAXM];
bool vis[MAXN][2];
int top=1;
int st[MAXN];
bool inst[MAXN][2];
bool Dfs(int x,bool v){//点x,黑边为奇数/偶数
if(vis[x][v]){
return false;
}
vis[x][v]=true;
inst[x][v]=true;
for(int i=head[x];i;i=nxt[i])
{
if(!w[i]){
continue;
}
bool tov=v^c[i];
if(inst[to[i]][!tov]){
w[i]=!w[i];
w[i^1]=!w[i^1];
while(x!=to[i]||v==tov)
{
int now=st[top];
top--;
w[now]^=1;
w[now^1]^=1;
v^=c[now];
x=to[now^1];
}
return true;
}
st[++top]=i;
if(Dfs(to[i],tov)){
return true;
}
--top;
}
inst[x][v]=false;
return false;
}
inline bool extra_test(){//找奇环
top=0;
memset(vis,false,sizeof(vis));
memset(inst,false,sizeof(inst));
for(int i=1;i<=n;i++)
{
if(Dfs(i,0)){
return true;
}
}
return false;
}
inline void solve(){
idx=1;
memset(head,0,sizeof(head));
scanf("%d%d",&n,&m);
s=n*2+1;
t=n*2+2;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d%d",&x,&y,&c[i*2]);
c[i*2+1]=c[i*2];
add_edge(x,y,1);
add_edge(y,x,0);
}
for(int i=1;i<=n;i++)
{
add_edge(s,i,1);
add_edge(i,s,0);
}
for(int i=n+1;i<=n+n;i++)
{
add_edge(i,t,1);
add_edge(t,i,0);
}
int flow=dinic();
if(flow!=n){
puts("-1");
return ;
}
bool flg=false;
for(int i=1;i<=m;i++)
{
if(!w[i*2]){
e[to[i*2+1]]=i;
if(c[i*2]){
flg=!flg;
}
}
}
if(!flg){
for(int i=1;i<=n;i++)
{
printf("%d ",e[i]);
}
putchar('\n');
return ;
}
if(extra_test()){
for(int i=1;i<=m;i++)
{
if(!w[i*2]){
e[to[i*2+1]]=i;
}
}
for(int i=1;i<=n;i++)
{
printf("%d ",e[i]);
}
putchar('\n');
}
else{
puts("-1");
}
}
signed main(){
int t;
scanf("%d",&t);
while(t--)
{
solve();
}
return 0;
}
暂时是最优解。

浙公网安备 33010602011771号