【Codeforces 576E】Painting Edges

线段树分治

题目大意:

给定一张无向图和 $k$ 种颜色,每条边开始时没有颜色,后来会涂上颜色。

需要保证,任意时刻,对任意一种颜色,只保留该种颜色的边,原图是一张二分图。

有 $q$ 次操作,每次会将一条边染成一种颜色,如果染色后合法则染,否则不染。

需要对每次染色操作输出是否染色成功。

题解:

线段树分治。

由于本题每条边的出现时间不确定,因此我们一边进行分治一边插入边。

具体地,用 $k$ 个可撤销并查集维护每种颜色的边的信息。需要支持查询两点之间距离的奇偶性。

递归的时候,先向时间早的结点递归,到了叶子结点,判断这条边是否能加入。如果可以,那么这条边在下一个时刻开始的一段区间内存在;如果不可以,那么这条边原本应该存在的时间,都仍然是原来的颜色。把新的边加入分治结构中即可。

由于保证插入的边所对应的时间范围一定在当前时间之后,因此不会出现冲突。

关于如何用并查集维护两点之间距离的奇偶性。我们对并查集上每个结点,记录 $d_i$ 表示 $i$ 到其并查集上父亲原图上距离的奇偶性。如果两个点本身不在同一个并查集中那显然没问题。如果在的话,连上这条边后,他们的距离的奇偶性就是 $i$ 到并查集根的奇偶性,异或 $j$ 到并查集根的奇偶性。连边的时候更新距离时,也这样计算即可。

时间复杂度 $O(nk+q\log q\log n)$,空间复杂度 $O(nk+q\log q)$。

Code:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N=5e5+5;
int n,m,U[N],V[N],q,k,X[N],Y[N],lst[N],col[N],nxt[N];
pair<int,int>sta[N];
int top;
struct Bc{
    int fa[N],sz[N];
    bool dis[N];
    inline int find(int x){while(x!=fa[x])x=fa[x];return x;}
    inline int getdist(int x){int y=0;while(x!=fa[x])y^=dis[x],x=fa[x];return y;}
    void init(){
        for(int i=1;i<=n;++i)fa[i]=i,sz[i]=1;
    }
}g[52];
struct info{
    int x,y,col;
};
vector<info>vec[N<<2];
void add(int l,int r,int o,const int&L,const int&R,const int&u,const int&v,const int&cl){
    if(L<=l&&r<=R)
        vec[o].push_back((info){u,v,cl});
    else{
        const int mid=l+r>>1;
        if(L<=mid)add(l,mid,o<<1,L,R,u,v,cl);
        if(mid<R)add(mid+1,r,o<<1|1,L,R,u,v,cl);
    }
}
void solve(int l,int r,int o){
    int TOP=top;
    for(int f=0;f<vec[o].size();++f){
        int cl=vec[o][f].col,u=vec[o][f].x,v=vec[o][f].y;
        int x=g[cl].find(u),y=g[cl].find(v);
        if(x!=y){
            if(g[cl].sz[x]<g[cl].sz[y])swap(x,y);
            g[cl].sz[x]+=g[cl].sz[y];
            g[cl].dis[y]=g[cl].getdist(u)^g[cl].getdist(v)^1;
            g[cl].fa[y]=x;
            sta[++top]=make_pair(cl,y);
        }
    }
    if(l==r){
        int id=X[l],cl=Y[l];
        int u=U[id],v=V[id];
        bool dis=g[cl].getdist(u)^g[cl].getdist(v)^1;
        if(dis&&g[cl].find(u)==g[cl].find(v)){
            cout<<"NO\n";
            if(l+1<=nxt[l])
                add(1,q,1,l+1,nxt[l],u,v,col[id]);
        }
        else{
            cout<<"YES\n";
            if(l+1<=nxt[l])
                add(1,q,1,l+1,nxt[l],u,v,col[id]=cl);
        }
    }else{
        const int mid=l+r>>1;
        solve(l,mid,o<<1),solve(mid+1,r,o<<1|1);
    }
    while(top>TOP){
        pair<int,int>f=sta[top--];
        int cl=f.first,y=f.second;
        g[cl].sz[g[cl].fa[y]]-=g[cl].sz[y];
        g[cl].dis[y]=0;
        g[cl].fa[y]=y;
    }
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m>>k>>q;
    for(int i=1;i<=m;++i)
        cin>>U[i]>>V[i];
    for(int i=1;i<=q;++i){
        cin>>X[i]>>Y[i];
        if(lst[X[i]]){
            nxt[lst[X[i]]]=i;
        }
        lst[X[i]]=i;
    }
    for(int i=1;i<=q;++i)
        if(!nxt[i])nxt[i]=q;
    for(int i=1;i<=k;++i)g[i].init();
    solve(1,q,1);
    return 0;
}