题面
题意:N个点M条边的无向图,询问保留图中编号在[l,r]的边时图中的联通块个数,强制在线。
这是一道LCT好题吧,(我觉得标签有两个或以上,代码很长但还能1A的题就是好题…)。
连通块只要维护出一棵树便能保持连通,而LCT处理图问题时的精髓正在于形成环时要把环上的哪条边Cut掉。
假设现在来了一条编号为i的边,考虑所有r=i的询问。
若i这条边加进来后没有形成环,显然对于所有l,有询问[l,i]=l[i,-1]-1。
若i这条边形成了环,是对于所有l,询问[l,i]=l[i,-1]吗?
显然不是,假设环上有一条编号为j的边,在j没有加进来的时候,i同样连接了两个连通块,使得答案-1。
设环上编号最小的边的编号为k,显然,对于所有p<=k
有询问[p,i]=[p,i-1],对于所有p>k,有[p,i]=[p,i-1]-1。
在i加入后,对于所有r>i的询问,k并不会对图的连通块数量产生影响,故可以直接删除
下面考虑如何统计答案,我们对于每个r建一棵线段树,发现第r棵线段树和r-1棵线段树只有一个区间-1。用常见的差分套路,把区间-1改为单点修改,单点查询改为求前缀和。那么第r棵线段树和r-1棵线段树非常相似,可以用线段树的可持久化操作,每次修改新增一条链,保留历史版本,保证强制在线。
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))
typedef long long LL;
const int N=200200,oo=1e9;
int n,m,ops,k,cnt=0;
void read(int &hy)
{
hy=0;
char cc=getchar();
while(cc<'0'||cc>'9')
cc=getchar();
while(cc>='0'&&cc<='9')
{
hy=(hy<<3)+(hy<<1)+cc-48;
cc=getchar();
}
}
struct tree
{
tree *c[2],*f,*pp,*A,*B;
int num,minv;
bool flip;
int d(){return f->c[1]==this;}
void sc(tree *x,int d){ (c[d]=x)->f=this;}
}nil[2*N],*ro[N],*stack[2*N],*edge[N];
tree *newtree(int k)
{
nil->num=nil->minv=oo;
nil->c[0]=nil->c[1]=nil->f=nil->pp=nil->A=nil->B=nil;
nil[++cnt]=nil[0];
nil[cnt].num=nil[cnt].minv=k;
return nil+cnt;
}
void down(tree *x)
{
if(x->flip)
{
x->flip=0;
swap(x->c[0],x->c[1]);
x->c[0]->flip^=1;
x->c[1]->flip^=1;
}
}
void up(tree *x)
{
x->minv=min(x->num,min(x->c[0]->minv,x->c[1]->minv));
}
void zig(tree *x)
{
int d=x->d();
tree *y=x->f;
y->sc(x->c[!d],d);
if(y->f!=nil)
y->f->sc(x,y->d());
else
x->f=nil;
x->sc(y,!d);
x->pp=y->pp;
y->pp=nil;
up(y);
up(x);
}
void splay(tree *x)
{
int hy=1;
stack[1]=x;
while(stack[hy]->f!=nil)
{
hy++;
stack[hy]=stack[hy-1]->f;
}
while(hy)
{
down(stack[hy]);
hy--;
}
for(tree *y;x->f!=nil;)
{
y=x->f;
if(y->f!=nil)
(x->d() ^ y->d()) ? zig(x) : zig(y);
zig(x);
}
}
void Access(tree *x)
{
tree *y=nil;
while(x!=nil)
{
splay(x);
if(x->c[1]!=nil)
{
x->c[1]->f=nil;
x->c[1]->pp=x;
}
x->c[1]=y;
if(y!=nil)
y->f=x;
up(x);
y->pp=nil;
y=x;
x=x->pp;
}
}
void Evert(tree *x)
{
Access(x);
splay(x);
x->flip^=1;
}
void Link(tree *x,tree *y)
{
Evert(y);
splay(y);
y->pp=x;
}
void Cut(tree *x,tree *y)
{
Evert(x);
Access(y);
splay(y);
y->c[0]->f=nil;
y->c[0]=nil;
up(y);
}
tree *Findro(tree *x)
{
Access(x);
splay(x);
while(x->c[0]!=nil)
x=x->c[0];
splay(x);
return x;
}
int segtree[40*N],cur,L[40*N],R[40*N],gen[N];
void update(int &ro,int l,int r,int p,int ad)
{
segtree[++cur]=segtree[ro];
L[cur]=L[ro];
R[cur]=R[ro];
ro=cur;
segtree[ro]+=ad;
if(l==r)
return;
int mid=(l+r)/2;
if(mid>=p)
update(L[ro],l,mid,p,ad);
else
update(R[ro],mid+1,r,p,ad);
}
int query(int ro,int l,int r,int zuo,int you)
{
if(you<l || r<zuo)
return 0;
if(l>=zuo&&r<=you)
return segtree[ro];
int mid=(l+r)/2;
return query(L[ro],l,mid,zuo,you)+query(R[ro],mid+1,r,zuo,you);
}
int main()
{
cin>>n>>m>>k>>ops;
for(int i=1;i<=n;i++)
ro[i]=newtree(oo);
for(int i=1;i<=m;i++)
{
gen[i]=gen[i-1];
int a,b;
read(a);
read(b);
if(a==b)
continue;
if(Findro(ro[a])==Findro(ro[b]))
{
Evert(ro[b]);
Access(ro[a]);
splay(ro[a]);
int sm=ro[a]->minv;
tree *tu=edge[sm];
Cut(tu->A,tu);
Cut(tu->B,tu);
update(gen[i],1,m+1,sm+1,1);
}
else
update(gen[i],1,m+1,1,1);
tree *hy=newtree(i);
edge[i]=hy;
hy->A=ro[a];
hy->B=ro[b];
Link(hy,ro[a]);
Link(hy,ro[b]);
update(gen[i],1,m+1,i+1,-1);
}
int ans=0;
for(int i=1;i<=k;i++)
{
int a,b;
read(a);
read(b);
if(ops)
{
a^=ans;
b^=ans;
}
ans=n-query(gen[b],1,m+1,1,a);
printf("%d\n",ans);
}
return 0;
}