LGP10777 反色刷 学习笔记

LGP10777 反色刷 学习笔记

Luogu Link

题意简述

给定一个 \(n\)\(m\) 边的简单无向图。边有黑白两种颜色。需要支持两种操作共 \(q\) 次:

  • 1 x:将第 \(x\) 条边反色。
  • 2 求出以“回路反色操作”将图刷成全白的所需要的最少次数。字面意思地,“回路反色操作”就是从任意点出发,每经过一条边则将其反色,最后需回到起点。如果不存在可行方案,输出 -1

\(n,m,q\le 10^6\)

做法解析

首先我们先来解决下有无解判定的问题。这题看着就和欧拉回路相关,所以往相关东西上面靠,比如考虑点的度数。事实也的确如此,有解当且仅当每个点连接的黑边数量为偶数,由欧拉回路知识证明显然。

然后我们再来考虑下答案上下界相关的问题。我们发现,一个极大的仅包含黑边的连通块显然是可以一笔画的;我们又发现,如果两个极大的仅包含黑边的连通块在原图里面可以连通,那我们可以把它们一起一笔画了(具体来说,就是我们先把黑连通块一画完,再走一段路到黑连通块二,把黑连通块二画完,再顺着刚才的路原路返回,这样两个黑连通块都被干掉,中间那段路走两次抵消了)。

所以得到结论:在有解的情况下,答案即为原图中存在黑边的连通块个数。

代码实现不需要我多讲了吧,详见代码。

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=1e6+5;
int N,M,Q,X,Y,Z,deg[MaxN],Opt;
struct edge{int u,v,w;}E[MaxN];
struct UnionFind{
    int n,ufa[MaxN];
    void init(int x){n=x;for(int i=1;i<=n;i++)ufa[i]=i;}
    int find(int u){return ufa[u]==u?u:ufa[u]=find(ufa[u]);}
    void merge(int u,int v){ufa[find(v)]=find(u);}
}UniFd;
int csum,ans,bla[MaxN];
void addudge(int u,int v,int w,int i){
    E[i]={u,v,w};UniFd.merge(u,v);
    csum-=(deg[u]+deg[v]);deg[u]^=w,deg[v]^=w;csum+=(deg[u]+deg[v]);
}
int main(){
    readis(N,M),UniFd.init(N);
    for(int i=1;i<=M;i++)readis(X,Y,Z),addudge(X,Y,Z,i);
    for(int i=1,fu;i<=M;i++)if(E[i].w)fu=UniFd.find(E[i].u),ans+=(!bla[fu]),bla[fu]++;
    readi(Q);while(Q--){
        readi(Opt);
        if(Opt==1){
            readi(X),X++;auto [u,v,w]=E[X];int fu=UniFd.find(u);
            csum-=(deg[u]+deg[v]);deg[u]^=1,deg[v]^=1;csum+=(deg[u]+deg[v]);
            if(E[X].w)E[X].w=0,bla[fu]--,ans-=(!bla[fu]);
            else E[X].w=1,ans+=(!bla[fu]),bla[fu]++;
        }
        if(Opt==2)writil(csum?-1:ans);
    }
    return 0;
}
posted @ 2025-05-04 09:43  矞龙OrinLoong  阅读(6)  评论(0)    收藏  举报