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

55 篇文章 0 订阅
41 篇文章 0 订阅

题目描述

传送门

题目大意:给出一个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);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值