题目描述:
题目传送门
N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。
n,m<=200000
题目分析:
图的连通块个数肿么求?
按编号顺序依次加边。
如果边
i
i
i加入的时候形成了环,那么把环中最先加入的边
j
j
j找到,令
p
r
e
[
i
]
=
j
pre[i]=j
pre[i]=j,并把
j
j
j断掉。
如果没有成环,那么
p
r
e
[
i
]
=
0
pre[i]=0
pre[i]=0
区间
[
l
,
r
]
[l,r]
[l,r]的边中连通两个连通块的边就是那些
p
r
e
[
i
]
<
l
pre[i]<l
pre[i]<l的边。
如果pre[i]>=l,说明这个连通块中最先加入的边都>=l,那么i一定没有造成贡献
如果pre[i]<l,那么l到i-1的边一定不能构成原来完整的连通块,i一定会造成贡献
(这个过程类似于做"区间中出现过的数的贡献")
加边断边的过程用LCT维护,求在 [ l , r ] [l,r] [l,r]内且 p r e [ i ] < l pre[i]<l pre[i]<l的 i i i的个数用主席树查询。
然而我的maxn刚好开200000,极限数据爆炸,硬是调了3h+,把整段代码全部改掉就是没有改maxn,自闭。。。
Code:
#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
const int maxn = 200005;
const int maxp = maxn*20;
using namespace std;
inline void read(int &a){
char c;bool f=0;
while(!isdigit(c=getchar())) if(c=='-') f=1;
for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
if(f) a=-a;
}
namespace LCT{
const int N = 400005;
int ch[N][2],fa[N],v[N],pos[N],tot,nb[N][2];
bool rev[N];
#define il inline
#define lc ch[x][0]
#define rc ch[x][1]
il bool isr(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
il bool isc(int x){return ch[fa[x]][1]==x;}
il void upd(int x){
pos[x]=x;
if(v[pos[x]]>v[pos[lc]]) pos[x]=pos[lc];
if(v[pos[x]]>v[pos[rc]]) pos[x]=pos[rc];
}
il void pushdown(int x){
if(rev[x]){
swap(lc,rc),rev[x]=0;
if(lc) rev[lc]^=1; if(rc) rev[rc]^=1;
}
}
il void pdpath(int x){if(!isr(x)) pdpath(fa[x]);pushdown(x);}
il void rot(int x){
int y=fa[x],z=fa[y],c=isc(x);
if(!isr(y)) ch[z][isc(y)]=x;
(ch[y][c]=ch[x][!c])&&(fa[ch[x][!c]]=y);
fa[ch[x][!c]=y]=x,fa[x]=z;
upd(y),upd(x);
}
il void splay(int x){
pdpath(x);
for(;!isr(x);rot(x))
if(!isr(fa[x])) rot(isc(fa[x])==isc(x)?fa[x]:x);
}
il int access(int x,int y=0){
for(;x;x=fa[y=x]) splay(x),ch[x][1]=y,upd(x);
return y;
}
il void bert(int x){access(x),splay(x),rev[x]^=1;}
il int sert(int x){access(x),splay(x);for(;lc;x=lc);return x;}
il void link(int x,int y){bert(x),fa[x]=y;}
il void cut(int x,int y){bert(x),access(y),splay(y);fa[x]=ch[y][0]=0,upd(y);}
il int split(int x,int y){bert(x),access(y),splay(y); return y;}
il bool judge(int x,int y){bert(x);return sert(y)==x;}
il int insert(int x,int y,int w){
int tmp=0;
if(judge(x,y)){
int p=pos[split(x,y)]; tmp=v[p];
cut(nb[p][0],p),cut(p,nb[p][1]);
}
v[++tot]=w,pos[tot]=tot,nb[tot][0]=x,nb[tot][1]=y;
link(x,tot),link(tot,y);
return tmp;
}
#undef lc
#undef rc
}
int n,m,Q,tp,ans;
int rt[maxn],lc[maxp],rc[maxp],s[maxp],tot;
void insert(int &now,int l,int r,int pos){
s[++tot]=s[now]+1,lc[tot]=lc[now],rc[tot]=rc[now];
now=tot;
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) insert(lc[now],l,mid,pos);
else insert(rc[now],mid+1,r,pos);
}
int query(int now,int p,int l,int r,int x){
if(x==r||!now) return s[now]-s[p];
int mid=(l+r)>>1;
if(x<=mid) return query(lc[now],lc[p],l,mid,x);
else return s[lc[now]]-s[lc[p]]+query(rc[now],rc[p],mid+1,r,x);
}
int main()
{
int x,y;
read(n),read(m),read(Q),read(tp),LCT::tot=n;
for(int i=0;i<=n;i++) LCT::v[i]=m+1;
for(int i=1;i<=m;i++){
read(x),read(y);
if(x==y) {rt[i]=rt[i-1];continue;}
insert(rt[i]=rt[i-1],0,m-1,LCT::insert(x,y,i));
}
while(Q--){
read(x),read(y);if(tp) x^=ans,y^=ans;
printf("%d\n",ans=n-query(rt[y],rt[x-1],0,m-1,x-1));
}
}