[LCT+可持久化线段树]Chef and Graph Queries

考虑加入一条边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的,显然会是第二种情况。
O ( n l o g 2 n ) O(nlog_{2}n)

#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;
}

猜你喜欢

转载自blog.csdn.net/ezoilearner/article/details/82946838