题目大意
给定一张图,每次询问编号在[l,r]的边连上后会形成多少联通块。
离线做法
我们考虑莫队。
考虑左端点所在的每一块,左端点到块末最多根号的距离,右端点是单调的。并查集容易添加,因此只保留块末到右端点的联通情况,每次暴力加上块末到左端点部分。
在线做法
把加入1~i条边,每条边权值设置为编号,所形成的最大生成树叫第i个版本。
[l,r]的答案=第r个版本中联通块数量+第r个版本中[1,l-1]存在树中的数量
依次加上每条边,用LCT维护得到每个版本,然后得到其联通块数量。可持久化线段树来保存每个版本中边的出现情况。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=200000+10,maxtot=8000000+10;
int father[maxn*2],key[maxn*2],num[maxn*2],tree[maxn*2][2],pp[maxn*2];
bool bz[maxn*2];
int ans[maxn],root[maxn],left[maxtot],right[maxtot],sum[maxtot],fa[maxn],edge[maxn][2],sta[maxn*2];
int i,j,k,l,r,t,n,m,q,tot,top,cnt,now;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int getfa(int x){
return fa[x]?fa[x]=getfa(fa[x]):x;
}
int pd(int x){
return tree[father[x]][1]==x;
}
void update(int x){
num[x]=x;
if (tree[x][0])
if (key[num[tree[x][0]]]<key[num[x]]) num[x]=num[tree[x][0]];
if (tree[x][1])
if (key[num[tree[x][1]]]<key[num[x]]) num[x]=num[tree[x][1]];
}
void rotate(int x){
int y=father[x],z=pd(x);
father[x]=father[y];
if (father[y]) tree[father[y]][pd(y)]=x;
tree[y][z]=tree[x][1-z];
if (tree[x][1-z]) father[tree[x][1-z]]=y;
tree[x][1-z]=y;
father[y]=x;
update(y);
update(x);
if (pp[y]){
pp[x]=pp[y];
pp[y]=0;
}
}
void clear(int x){
if (bz[x]){
bz[tree[x][0]]^=1;
bz[tree[x][1]]^=1;
swap(tree[x][0],tree[x][1]);
bz[x]=0;
}
}
void remove(int x,int y){
cnt=0;
while (x!=y){
sta[++cnt]=x;
x=father[x];
}
while (cnt){
clear(sta[cnt]);
cnt--;
}
}
void splay(int x,int y){
remove(x,y);
while (father[x]!=y){
if (father[father[x]]!=y)
if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);
rotate(x);
}
}
void access(int x){
int y;
splay(x,0);
if (tree[x][1]){
pp[tree[x][1]]=x;
father[tree[x][1]]=0;
tree[x][1]=0;
update(x);
}
while (x){
splay(x,0);
y=pp[x];
if (!y) break;
splay(y,0);
if (tree[y][1]){
pp[tree[y][1]]=y;
father[tree[y][1]]=0;
}
tree[y][1]=x;
pp[x]=0;
father[x]=y;
update(y);
x=y;
}
}
void makeroot(int x){
access(x);
splay(x,0);
bz[x]^=1;
}
int newnode(int x){
sum[++top]=sum[x];
left[top]=left[x];
right[top]=right[x];
return top;
}
void change(int &p,int l,int r,int a,int b){
p=newnode(p);
sum[p]+=b;
if (l==r) return;
int mid=(l+r)/2;
if (a<=mid) change(left[p],l,mid,a,b);else change(right[p],mid+1,r,a,b);
}
int query(int p,int l,int r,int a,int b){
if (a>b) return 0;
if (l==a&&r==b) return sum[p];
int mid=(l+r)/2;
if (b<=mid) return query(left[p],l,mid,a,b);
else if (a>mid) return query(right[p],mid+1,r,a,b);
else return query(left[p],l,mid,a,mid)+query(right[p],mid+1,r,mid+1,b);
}
void write(int x){
if (!x){
putchar('0');
putchar('\n');
return;
}
cnt=0;
while (x){
sta[++cnt]=x%10;
x/=10;
}
while (cnt){
putchar(sta[cnt]+'0');
cnt--;
}
putchar('\n');
}
int main(){
n=read();m=read();q=read();
tot=now=n;
fo(i,1,n) key[i]=m+1,num[i]=i;
fo(i,1,m){
j=read();k=read();
edge[i][0]=j;edge[i][1]=k;
root[i]=root[i-1];
tot++;
key[tot]=i;
num[tot]=tot;
if (j==k){
ans[i]=ans[i-1];
continue;
}
if (getfa(j)!=getfa(k)){
fa[getfa(k)]=getfa(j);
now--;
makeroot(k);
splay(k,0);
pp[k]=tot;
pp[tot]=j;
change(root[i],1,m,i,1);
}
else{
makeroot(j);
access(k);
splay(k,0);
l=num[k];
change(root[i],1,m,l-n,-1);
makeroot(l);
access(l);
splay(edge[l-n][0],0);
pp[edge[l-n][0]]=0;
splay(edge[l-n][1],0);
pp[edge[l-n][1]]=0;
makeroot(k);
splay(k,0);
pp[k]=tot;
pp[tot]=j;
change(root[i],1,m,i,1);
}
ans[i]=now;
}
while (q--){
l=read();r=read();
now=ans[r];
now+=query(root[r],1,m,1,l-1);
write(now);
}
}