[CF576E]Painting Edges

12 篇文章 0 订阅
8 篇文章 0 订阅

Description

给出一张n个点m条边的图,有k种颜色,给出q次操作,每次操作形如“将第i条边染成颜色c”
如果某一次操作之后会使得对于颜色c,只考虑颜色c的边,原图不是一个二分图,那么这次操作无效(即不会进行染色)
求每次操作是否成功。
n,m,q<=5*1e5,k<=20

Solution

首先这是一个动态二分图的问题,可以直接通过并查集+分治做到两个log
具体来说每条边有出现时间区间,按时间分治直接加边删边。
这里的删边都是删去刚刚加入的边,可以通过并查集按秩合并支持撤销。
然后这道题的问题是我们不知道每条边的实际存在时间区间,怎么办?
我们可以把每条边当做存在然后扔进分治结构,有序便利每一个子节点,如果当前操作是有效的,那么就把当前边的颜色更新。
然后我们把这条边的下一个时间区间扔进分治结构。
复杂度是相同的。

Code

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

int read() {
    char ch;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
    int x=ch-'0';
    for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x;
}

const int N=5*1e5+5,D=55;

int n,m,k,id,c,q,lst[N],x[N],y[N],stack[N<<1],col[N<<1],color[N],tot;

struct node{int r,c,e;}p[N];

struct DSU{
    int f[N],rank[N],val[N];
    int get(int x) {return f[x]?get(f[x]):x;}
    int find(int x) {return f[x]?(val[x]^find(f[x])):0;}
    void Union(int c,int ax,int ay) {
        int x=get(ax),y=get(ay);
        if (x==y) return;
        if (rank[x]<rank[y]) {
            stack[++tot]=x;col[tot]=c;
            val[x]=find(ax)^find(ay)^1;f[x]=y;
        } else {
            stack[++tot]=y;col[tot]=c;
            val[y]=find(ax)^find(ay)^1;f[y]=x;
            if (rank[x]==rank[y]) {
                rank[x]++;
                stack[++tot]=-x;col[tot]=c;
            }
        }
    }
}d[D];

void remove(int now) {
    for(;tot>now;tot--) 
        if (stack[tot]>0) d[col[tot]].f[stack[tot]]=0;
        else d[col[tot]].rank[-stack[tot]]--;
}

vector< pair<int,int> > que[N<<2];

void insert(int v,int l,int r,int x,int y,pair<int,int> s) {
    if (x>y) return;
    if (l==x&&r==y) {que[v].push_back(s);return;}
    int mid=l+r>>1;
    if (y<=mid) insert(v<<1,l,mid,x,y,s);
    else if (x>mid) insert(v<<1|1,mid+1,r,x,y,s);
    else insert(v<<1,l,mid,x,mid,s),insert(v<<1|1,mid+1,r,mid+1,y,s);
}

void solve(int v,int l,int r) {
    int now=tot;
    for(int i=0;i<que[v].size();i++) {
        int c=que[v][i].first,e=que[v][i].second;
        d[c].Union(c,x[e],y[e]);
    }
    if (l==r) {
        int ax=x[p[l].e],ay=y[p[l].e],c=p[l].c;
        if (d[c].get(ax)!=d[c].get(ay)||d[c].find(ax)!=d[c].find(ay)) {
            puts("YES");
            color[p[l].e]=p[l].c;
        } else puts("NO");
        insert(1,1,q,l+1,p[l].r,pair<int,int>(color[p[l].e],p[l].e));
        remove(now);
        return;
    }
    int mid=l+r>>1;
    solve(v<<1,l,mid);
    solve(v<<1|1,mid+1,r);
    remove(now);
}

int main() {
    n=read();m=read();k=read();q=read();
    fo(i,1,m) x[i]=read(),y[i]=read();
    fo(i,1,q) {
        id=read();c=read();
        if (lst[id]) p[lst[id]].r=i-1;
        p[i].e=id;p[i].c=c;p[i].r=q;
        lst[id]=i;
    }
    solve(1,1,q);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值