[BZOJ3706]反色刷(并查集+欧拉图)

    xiaoxiao2021-04-14  33

    题目描述

    传送门

    题目大意:给出一个n个点m条边的无向图,每一条边有一个颜色0/1,每一次可以选择一个环(不一定是简单环)使环上的所有边都反色。问最少选择多少次使所有的边都变成白色。这个图的边的颜色会发生变化,每一次操作有可能使一条边反色。

    题解

    欧拉回路,比较显然的一点是有解的充要条件是没有奇点 刚开始一直在往维护黑边的连通块个数的方面考虑,然后就一直在想什么写个lct啊… 但实际上这样做是有一点问题的,因为白边不一定不走只要走偶数次就可以 那么可以将一条白边看成两条黑边,这样的话对每个点的奇偶性是没有影响的,而且同样是求欧拉回路 用并查集先维护出连通块了之后,只需要记录一下每一个连通块是否有黑边,如果有黑边的话就需要刷一次,没有黑边的话就不需要,无解的话同样用度数的奇偶性判断

    代码

    #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; #define N 1000005 int n,m,q,ans,du[N],f[N],size[N],cnt[2]; struct data{int x,y,z;}e[N]; int find(int x) { if (x==f[x]) return x; f[x]=find(f[x]); return f[x]; } void merge(int x,int y) { int f1=find(x),f2=find(y); if (f1!=f2) f[f1]=f2; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;++i) f[i]=i; for (int i=1;i<=m;++i) { int x,y,z;scanf("%d%d%d",&x,&y,&z); if (!z) du[x]+=2,du[y]+=2; else ++du[x],++du[y]; merge(x,y); e[i].x=x,e[i].y=y,e[i].z=z; } for (int i=1;i<=n;++i) ++cnt[du[i]&1]; for (int i=1;i<=m;++i) { int x=e[i].x,y=e[i].y,z=e[i].z; int fa=find(x); if (z) { if (!size[fa]) ++ans; ++size[fa]; } } scanf("%d",&q); while (q--) { int opt;scanf("%d",&opt); if (opt==1) { int id;scanf("%d",&id);++id; int x=e[id].x,y=e[id].y,z=e[id].z; int fa=find(x); if (!z) { --cnt[du[x]&1],--cnt[du[y]&1],--du[x],--du[y],++cnt[du[x]&1],++cnt[du[y]&1]; if (!size[fa]) ++ans; ++size[fa]; } else { --cnt[du[x]&1],--cnt[du[y]&1],++du[x],++du[y],++cnt[du[x]&1],++cnt[du[y]&1]; --size[fa]; if (!size[fa]) --ans; } e[id].z^=1; } else { if (cnt[1]) puts("-1"); else printf("%d\n",ans); } } }
    转载请注明原文地址: https://ju.6miu.com/read-670037.html

    最新回复(0)