考虑加入一条边i时会发生什么情况:
1.合并了两个联通块,ai为0
2.在一个联通块内,并且是在一个环上(归纳保证),那我们删去该环上编号最小的边,ai,联通性不变
上面的可以用LCT实现
查询时,我们的答案为n-l~r中有多少个ai小于l
因为,对于l~r中小于l的ai,加入i会是第一种情况
其余会是第二种情况
证明???
我们从l~r顺序加边,同时删掉对应的ai
当前如果进行到第j边,那么加入l~j的边形成的图会是加入1~j边的子集。
这就证明了对于其中ai<l的i,它会是属于其中第一种情况的。
对于ai>=l的,显然会是第二种情况。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<algorithm>
using namespace std;
const int inf=2147483647;
int n,m,q;
#define Maxn 200010
#define V 4000010
struct Edge{
int s,e;
}edge[Maxn];
struct Node{
int son[2],fa;
int minv;
int val;
int rev;
}tree[Maxn*2];
inline bool isrt(int k){return tree[tree[k].fa].son[0]!=k&&tree[tree[k].fa].son[1]!=k;}
inline int who(int k){return tree[tree[k].fa].son[1]==k;}
inline void push_up(int k){tree[k].minv=min(tree[k].val,min(tree[tree[k].son[0]].minv,tree[tree[k].son[1]].minv));}
inline void push_down(int k){
if(tree[k].rev){
swap(tree[tree[k].son[0]].son[0],tree[tree[k].son[0]].son[1]);
tree[tree[k].son[0]].rev^=1;
swap(tree[tree[k].son[1]].son[0],tree[tree[k].son[1]].son[1]);
tree[tree[k].son[1]].rev^=1;
tree[k].rev=0;
}
}
inline void rot(int u){
int f=tree[u].fa,ff=tree[f].fa;
int dir=who(u);
tree[u].fa=ff;
if(!isrt(f))tree[ff].son[who(f)]=u;
tree[f].son[dir]=tree[u].son[dir^1];tree[tree[u].son[dir^1]].fa=f;
tree[u].son[dir^1]=f;tree[f].fa=u;
push_up(f);
push_up(u);
}
stack<int> st;
inline void Splay(int u){
st.push(u);
for(int x=u;!isrt(x);x=tree[x].fa)st.push(tree[x].fa);
while(!st.empty()){
push_down(st.top());
st.pop();
}
for(int f;!isrt(u);rot(u))
if(!isrt(f=tree[u].fa))rot(who(f)==who(u)?f:u);
}
inline void access(int u){
for(int t=0;u;t=u,u=tree[u].fa){
Splay(u);
tree[u].son[1]=t;
push_up(u);
}
}
inline void makeroot(int x){
access(x);
Splay(x);
tree[x].rev^=1;
swap(tree[x].son[0],tree[x].son[1]);
}
inline void link(int a,int b){
makeroot(a);
tree[a].fa=b;
}
inline bool isconnect(int a,int b){
makeroot(a);
access(b);
Splay(b);
return tree[a].fa;
}
inline bool cut(int a,int b){
makeroot(a);
access(b);
Splay(b);
tree[b].son[0]=0;
tree[a].fa=0;
push_up(b);
}
inline bool linkEdge(int eid){
int x=n+eid;
link(edge[eid].s,x);
link(x,edge[eid].e);
}
inline void cutEdge(int eid){
int x=n+eid;
cut(edge[eid].s,x);
cut(x,edge[eid].e);
}
inline int AddEdge(int eid){
int a=edge[eid].s,b=edge[eid].e;
if(!isconnect(a,b)){
linkEdge(eid);
return 0;
}
makeroot(a);
access(b);
Splay(b);
int ans=tree[b].minv;
cutEdge(ans);
linkEdge(eid);
return ans;
}
struct node{
int ls,rs;
int sumv;
}T[V];
int cnt;
int root[Maxn];
void insert(int &k,int x,int l,int r,int pos){
k=++cnt;
T[k]=T[x];
T[k].sumv++;
if(l==r)return;
int mid=(l+r)>>1;
if(pos<=mid)insert(T[k].ls,T[x].ls,l,mid,pos);
else insert(T[k].rs,T[x].rs,mid+1,r,pos);
}
int Query(int k1,int k2,int l,int r,int L,int R){
if(l==L&&r==R)return T[k2].sumv-T[k1].sumv;
int mid=(l+r)>>1;
if(R<=mid)return Query(T[k1].ls,T[k2].ls,l,mid,L,R);
else if(mid<L)return Query(T[k1].rs,T[k2].rs,mid+1,r,L,R);
else return Query(T[k1].ls,T[k2].ls,l,mid,L,mid)+Query(T[k1].rs,T[k2].rs,mid+1,r,mid+1,R);
}
inline void rd(int &x){
x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
tree[0].val=inf;tree[0].minv=inf;
rd(n);rd(m);rd(q);
for(register int i=1;i<=n+m;++i){
tree[i].son[0]=tree[i].son[1]=tree[i].fa=0;
tree[i].rev=0;
if(i<=n)tree[i].val=inf;
else tree[i].val=i-n;
tree[i].minv=tree[i].val;
}
cnt=0;
for(register int i=1;i<=m;++i){
rd(edge[i].s);rd(edge[i].e);
int ans;
if(edge[i].s!=edge[i].e)ans=AddEdge(i);
else ans=m;
insert(root[i],root[i-1],0,m,ans);
}
int l,r;
for(int i=1;i<=q;++i){
rd(l);rd(r);
printf("%d\n",n-Query(root[l-1],root[r],0,m,0,l-1));
}
}
return 0;
}