1. 程式人生 > >Code Chef - Chef and Graph Queries

Code Chef - Chef and Graph Queries

lin har space spl chroot www put name esp

傳送門

題目大意

給定一個$n$個點$m$條邊的無向圖$(n,m\leq 200000)$。

有$q$每次詢問$(q\leq 200000)$,每次給定一個區間$L,R$,求僅保留編號$\in[L,R]$的邊,原圖連通塊的數量。

題解

不難發現連通塊數量可以通過總點數$-$最大生成森林的邊集大小得到。

按照編號對邊從小到大排序,用$LCT$動態維護最大生成森林,每次操作加邊時,若兩個點不連通,就直接連邊即可。

否則,就把路徑上編號最小的邊斷掉,再強行連上新的邊。則當前的生成森林一定是最大的並且恰好覆蓋了每一個連通塊。

對於每一次詢問,就是用$n$減去在最大的邊編號為$R$時,最大生成森林中編號$\in[L,R]$的數量。

用主席樹維護一下即可。復雜度$O((m+q)\log n)$。

由於在$LCT$中維護邊權比較復雜,所以我們可以把一條邊變成一個點,這個點連向原邊的兩段端點,點權即為邊權,會方便許多。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 400020
#define INF 3000020
#define ls c[x][0]
#define rs c[x][1]
#define mid ((l+r)>>1)
using namespace std;
int read(){
	int nm=0,fh=1; char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw==‘-‘) fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-‘0‘);
	return nm*fh;
}
void write(int x){if(x>9) write(x/10);putchar(x%10+‘0‘);}
int n,m,fa[M],c[M][2],u[M],v[M],e[M],rev[M];
int L[M*30],R[M*30],sum[M*30],cnt,rt[M],S[M],top,tot;
void pushup(int x){if(x) e[x]=(x>n?x-n:INF),e[x]=min(e[x],min(e[ls],e[rs]));}
void pushdown(int x){if(rev[x]&&x) rev[x]=0,rev[ls]^=1,rev[rs]^=1,swap(ls,rs);}
bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
void rotate(int x){
	int tp=fa[x],dtp=fa[fa[x]],ms,ds;
	if(c[dtp][0]==tp) c[dtp][0]=x;
	else if(c[dtp][1]==tp) c[dtp][1]=x;
	if(c[tp][0]==x) ms=0,ds=1;else ms=1,ds=0;
	fa[x]=dtp,fa[tp]=x,fa[c[x][ds]]=tp;
	c[tp][ms]=c[x][ds],c[x][ds]=tp;
	pushup(tp),pushup(x);
}
void splay(int x){
	S[top=1]=x;
	for(int y=x;!isroot(y);y=fa[y]) S[++top]=fa[y];
	while(top) pushdown(S[top]),top--;
	while(!isroot(x)){
		int tp=fa[x];
		if(isroot(tp)) return rotate(x);
		else if(c[c[fa[tp]][0]][0]==x) rotate(tp);
		else if(c[c[fa[tp]][1]][1]==x) rotate(tp);
		else rotate(x);
	}
}
int fdrt(int x){return fa[x]?fdrt(fa[x]):x;}
void access(int x){for(int y=0;x;y=x,x=fa[x]) splay(x),rs=y,pushup(x);}
void chroot(int x){access(x),splay(x),rev[x]^=1;}
void link(int x,int y){chroot(x),splay(x),fa[x]=y;}
void cut(int x,int y){chroot(x),access(y),splay(x),fa[y]=ls=rs=c[y][0]=c[y][1]=0,pushup(x),pushup(y);}
int qry(int x,int y){chroot(x),access(y),splay(x);return (fdrt(y)!=x)?0:e[x];}
void ins(int &x,int pre,int l,int r,int pos,int dt){
	x=++cnt,L[x]=L[pre],R[x]=R[pre];
	sum[x]=sum[pre]+dt; if(l==r) return;
	if(pos<=mid)ins(L[x],L[pre],l,mid,pos,dt);
	else ins(R[x],R[pre],mid+1,r,pos,dt);
}
int getans(int x,int l,int r,int minn){
	if(r<minn||!sum[x]) return 0; if(l>=minn) return sum[x];
	return getans(L[x],l,mid,minn)+getans(R[x],mid+1,r,minn);
}
int main(){
	for(int T=read(),Q;T;T--,cnt=0){
		n=read(),m=read(),Q=read(),tot=n,memset(c,0,sizeof(c));
		memset(rt,0,sizeof(rt)),memset(fa,0,sizeof(fa)),memset(e,0x3f,sizeof(e));
		for(int i=1;i<=m;i++){
			int now; u[i]=read(),v[i]=read(),rt[i]=rt[i-1],tot++;
			if(u[i]==v[i]) continue; now=qry(u[i],v[i]);
			if(now)	ins(rt[i],rt[i],1,m,now,-1),cut(u[now],v[now]);
			ins(rt[i],rt[i],1,m,i,1),link(u[i],tot),link(v[i],tot);
		}
		while(Q--){
			int tl=read(),tr=read(),num;
			num=getans(rt[tr],1,m,tl);
			write(n-num),putchar(‘\n‘);
		}
	}
	return 0;
}

Code Chef - Chef and Graph Queries