CF576E Painting Edges(线段树分治)
------------恢复内容开始------------
[原题链接](https://www.luogu.com.cn/problem/CF576E)题意
给定一张 \(n\) 个点 \(m\) 条边的无向图。
一共有 \(k\) 种颜色,一开始,每条边都没有颜色。
定义合法状态为仅保留染成 \(k\) 种颜色中的任何一种颜色的边,图都是一张二分图。
有 \(q\) 次操作,第 \(i\) 次操作将第 \(e_i\) 条边的颜色染成 \(c_i\)。
但并不是每次操作都会被执行,只有当执行后仍然合法,才会执行本次操作。
你需要判断每次操作是否会被执行。
数据范围
\(n,m,q \leq 5 \times 10^5,k \leq 50\)。
思路
注意到本题中第 \(i\) 次染色操作只会对保留颜色为 \(c_i\) 的边能否形成二分图产生影响,同时颜色的总数也很小,就可以考虑类似于 P5787 的技巧,用 \(k\) 个扩展域并查集分别维护每一种颜色的情况。
考虑对于一条边相邻的染色操作(记为操作 \(i\) 和操作 \(j\)),操作 \(i\) 所能直接影响的范围为 \([i,j-1]\),而其对染色操作 \([i+1,j-1]\) 的影响取决于操作 \(i\) 能否被执行。
于是就可以将操作 \(i\) 覆盖到 \([i+1,j-1]\) 中,在分治到叶子节点时判断操作 \(i\) 能否被执行,如果不能被执行。此时虽然操作没有执行,但边还是存在的,还是需要判断原来的颜色是否为二分图。
可以发现操作不被执行实际上也就是把上一次执行的染色操作重新执行了一遍,那么直接把 \(c[i]\) 改为上一次操作的颜色即可。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=5e5+10,K=55;
vector<int>e[N<<2];
int pos[N],top,u[N],v[N],a[N],c[N],n,m,k,q,f[K][N<<1],height[K][N<<1];
int find(int c,int x){return f[c][x]==x?x:find(c,f[c][x]);}
struct node{int c,x,y,add;}stk[N];
void merge(int c,int x,int y)
{
if(x==y) return ;
if(height[c][x]>height[c][y]) swap(x,y);
stk[++top]=node{c,x,y,height[c][x]==height[c][y]};
f[c][x]=y;height[c][y]+=height[c][x]==height[c][y];
}
void update(int p,int l,int r,int nl,int nr,int k)
{
if(nl<=l&&r<=nr){e[p].push_back(k);return ;}
int mid=l+r>>1;
if(nl<=mid) update(p<<1,l,mid,nl,nr,k);
if(nr>mid) update(p<<1|1,mid+1,r,nl,nr,k);
}
void solve(int p,int l,int r)
{
int back=top,mid=l+r>>1;
for(auto i:e[p])
{
int fx=find(c[i],u[a[i]]),fy=find(c[i],v[a[i]]);
merge(c[i],find(c[i],u[a[i]]),find(c[i],v[a[i]]+n));
merge(c[i],find(c[i],u[a[i]]+n),find(c[i],v[a[i]]));
}
if(l==r)
{
if(find(c[r],u[a[r]])==find(c[r],v[a[r]])) puts("NO"),c[r]=pos[a[r]];
else puts("YES"),pos[a[r]]=c[r];
}
else solve(p<<1,l,mid),solve(p<<1|1,mid+1,r);
while(top>back)
{
int c=stk[top].c,x=stk[top].x,z=stk[top].add;top--;
height[c][f[c][x]]-=z;f[c][x]=x;
}
}
int main()
{
// freopen("in.txt","r",stdin);
scanf("%d%d%d%d",&n,&m,&k,&q);
for(int j=1;j<=k;j++)
for(int i=1;i<=n;i++)
f[j][i]=i,f[j][i+n]=i+n;
for(int i=1;i<=m;i++) scanf("%d%d",&u[i],&v[i]),pos[i]=q+1;
for(int i=1;i<=q;i++) scanf("%d%d",&a[i],&c[i]);
int cnt=0;
for(int i=q;i>=0;i--)
{
if(i+1<pos[a[i]]) update(1,1,q,i+1,pos[a[i]]-1,i);
//i+1是因为如果判断i时直接把它加进来并且能成功操作,这时的颜色是新的颜色,但后面撤销的就是原来的颜色,就会产生错误
pos[a[i]]=i;
}
memset(pos,0,sizeof(pos));solve(1,1,q);
return 0;
}
------------恢复内容结束------------
------------恢复内容结束------------
------------恢复内容结束------------