Loading

题解: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;
}

暂时是最优解。

posted @ 2025-02-09 07:09  Mathew_Miao  阅读(10)  评论(0)    收藏  举报