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

来源:互联网 发布:nginx rtmp hls 编辑:程序博客网 时间:2024/05/29 23:47

题目描述

传送门

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

题解

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

代码

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>using namespace std;#define N 1000005int 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);        }    }}
0 0
原创粉丝点击