首页 > 代码库 > bzoj3082: Graph2 离线+线段树

bzoj3082: Graph2 离线+线段树

经典问题。强制在线的话非常复杂。

考虑离线。

每条边的存在时间是一个区间,因此按时间建立一颗线段树,将每条边插入,拆成log条边。然后dfs线段树,每次并查集合并当前节点的所有边,到叶子节点时回答询问,回溯时撤销并查集的修改。

带撤销的并查集不能路径压缩,要按秩合并。

#include<bits/stdc++.h>#define N 200005#define M (l+r>>1)#define P (k<<1)#define S (k<<1|1)#define L l,M,P#define R M+1,r,S#define Z int l=1,int r=q,int k=1#define u first#define v secondusing namespace std;struct io_t{	operator int(){		int x;		scanf("%d",&x);		return x;	}}it;int n=it,m=it,s[N],t[N];int p[N],u[N],v[N],j,q;char d[N];typedef pair<int,int> vec;vec f[N],e[N];vector<vec> a[1<<18];int find(int i){	return p[i]==i	?i:find(p[i]);}void add(vec v,int s,int t,Z){	if(s<=l&&r<=t)		a[k].push_back(v);	else{		if(s<=M)			add(v,s,t,L);		if(t>M)			add(v,s,t,R);	}}void dfs(Z){	for(int j=0;j!=a[k].size();++j){		vec* i=&a[k][j];		i->u=find(i->u);		i->v=find(i->v);		if(i->u^i->v){			if(u[i->u]<u[i->v])				swap(i->u,i->v);			p[i->v]=i->u;			u[i->u]+=u[i->v];		}	}	if(l==r)		d[l]^81?0:v[l]		=find(f[l].u)		^find(f[l].v);	else{		dfs(L);dfs(R);	}	for(int j=a[k].size()-1;~j;--j){		vec* i=&a[k][j];		if(i->u^i->v){			p[i->v]=i->v;			u[i->u]-=u[i->v];		}	}}int main(){	for(int i=1;i<=n;++i)		u[p[i]=i]=1;	for(int i=1;i<=m;++i)		e[i]=vec(it,it);	q=it;	for(int i=1;i<=q;++i){		scanf(" %c%d",d+i,&j);		if(d[i]==68)			t[j]?0:t[j]=i;		if(d[i]==81)			f[i]=vec(j,it);		if(d[i]==73&&++m){			s[m]=i;			e[m]=vec(j,it);		}	}	for(int i=1;i<=m;++i)		add(e[i],		s[i],t[i]?t[i]:q);	dfs();	for(int i=1;i<=q;++i)		if(d[i]==81)			puts(v[i]?"No":"Yes");}

  

bzoj3082: Graph2 离线+线段树