[LOJ121][线段树分治][并查集][LCT](离线可过)动态图连通性

3 篇文章 0 订阅
2 篇文章 0 订阅

LOJ121

ETT是不可能ETT的,这辈子都不可能ETT的,所以就有了这道离线的

直接上线段树分治+并查集就完了
Code:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();};
	return res*f;
}
const int N=5e5+10;
int n,m,top,tot1,tot2;
int pt[N],siz[N],fa[N];
int que[N];
map<int,int>mp[N];
struct E{int x,y;}a[N],q[N],sta[N],de[N];
vector<int>tag[N<<2];
namespace bcj{
	int get(int v){return v==fa[v]?v:get(fa[v]);}
	inline void merge(int x,int y){
		x=get(x);y=get(y);
		if(x==y) return;
		if(siz[x]>siz[y]) swap(x,y);
		sta[++top]=(E){x,y};
		fa[x]=y;siz[y]+=siz[x];
	}
	inline void del(int tmp){
		while(top>tmp){
			int x=sta[top].x,y=sta[top--].y;
			fa[x]=x;siz[y]-=siz[x];
		}
	}
}
using namespace bcj;
namespace segtree{
	void ins(int k,int l,int r,int ql,int qr,int id){
		if(ql<=l && r<=qr) {tag[k].push_back(id);return;}
		int mid=(l+r)>>1;
		if(ql<=mid) ins(k<<1,l,mid,ql,qr,id);
		if(qr>mid) ins(k<<1|1,mid+1,r,ql,qr,id);	
	}
	void dfs(int k,int l,int r){
		int now=top;
		for(int i=0;i<tag[k].size();i++) merge(de[tag[k][i]].x,de[tag[k][i]].y);
		if(l==r){if(que[l]) puts(get(q[l].x)==get(q[l].y)?"Y":"N");}
		else{
			int mid=(l+r)>>1;
			dfs(k<<1,l,mid);dfs(k<<1|1,mid+1,r);
		}
		del(now);
	}
}
using namespace segtree;
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
	for(int i=1;i<=m;i++){
		int op=read(),x=read(),y=read();
		if(x>y) swap(x,y);
		if(op==0){
			if(pt[mp[x][y]]) continue;
			mp[x][y]=++tot1;
			a[tot1]=(E){x,y};
			pt[mp[x][y]]=i;
		}
		else if(op==1) {
			int id=mp[x][y];
			de[++tot2]=(E){x,y};
			ins(1,1,m,pt[id],i,tot2);
			pt[id]=0;
		}
		else q[i]=(E){x,y},que[i]=true;
	}
	for(int i=1;i<=tot1;i++) if(pt[i]){
		de[++tot2]=(E){a[i].x,a[i].y};
		ins(1,1,m,pt[i],m,tot2);
	}
	dfs(1,1,m);
	return 0;
}

还有一个做法是LCT,维护的是以删除时间为边权的最大生成树,因为如果一个边有一个时刻不在这个生成树上,那么它之后无论怎么变化对答案都不会产生影响,因为树总是连通的,而且树上所有边在它删除之前都不会消失

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int M=5e5+5,N=5e3+5;
struct Q{int op,x,y,tm;}q[M];
int n,m,x[N+M],y[N+M],v[N+M],cnt;
map<ll,int>hs;
namespace LCT{
	int fa[N+M],ls[N+M],rs[N+M],rev[N+M],s[N+M];
	int que[N+M];
	inline int isrs(int x){return x==rs[fa[x]];}
	inline bool isroot(int x){
		if(!fa[x]) return true;
		return ls[fa[x]]!=x && rs[fa[x]]!=x;
	}
	inline void pushup(int x){
		s[x]=x;
		if(v[s[ls[x]]]<v[s[x]]) s[x]=s[ls[x]];
		if(v[s[rs[x]]]<v[s[x]]) s[x]=s[rs[x]];
	}
	inline void pushdown(int x){
		if(rev[x]){
			swap(ls[x],rs[x]);
			if(ls[x]) rev[ls[x]]^=1;
			if(rs[x]) rev[rs[x]]^=1;
			rev[x]=0;
		}
	}
	inline void rotate(int x){
		int y=fa[x],z=fa[y],b=ls[y]==x?rs[x]:ls[x];
		if(z && !isroot(y)) (ls[z]==y?ls[z]:rs[z])=x;
		fa[x]=z,fa[y]=x;b?fa[b]=y:0;
		if(ls[y]==x) rs[x]=y,ls[y]=b;
		else ls[x]=y,rs[y]=b;
		pushup(y);pushup(x);
	}
	inline void splay(int x){
		que[que[0]=1]=x;
		for(int y=x;!isroot(y);y=fa[y]) que[++que[0]]=fa[y];
		for(int i=que[0];i;i--) pushdown(que[i]);
		while(!isroot(x)){
			while(!isroot(fa[x])){
				if(isrs(x)==isrs(fa[x])) rotate(fa[x]);
				else rotate(x);
			}
			rotate(x);
		}
		pushup(x);
	}
	inline void access(int x){
		for(int y=0;x;y=x,x=fa[x]){
			splay(x);rs[x]=y;
			if(y) fa[y]=x;
			pushup(x);
		}
	}
	inline int findroot(int x){
		access(x);splay(x);
		while(pushdown(x),ls[x]) x=ls[x];
		splay(x);return x;
	}
	inline void makeroot(int x){access(x);splay(x);rev[x]^=1;}
	inline void link(int x,int y){makeroot(x);fa[x]=y;}
	inline void split(int x,int y){makeroot(x);access(y);splay(y);}
	inline void cut(int x,int y){split(x,y);ls[y]=fa[x]=0;pushup(y);}
	inline int query(int x,int y){split(x,y);return s[y];}
}
using namespace LCT;
inline void add(int xx,int yy,int zz){
	s[++cnt]=cnt;v[cnt]=zz;
	x[cnt]=xx,y[cnt]=yy;
	link(cnt,xx);link(cnt,yy);
}
int main(){
	n=read();m=read();
	for(int i=1;i<=m;i++){
		q[i].op=read();q[i].x=read();q[i].y=read();
		if(q[i].x>q[i].y) swap(q[i].x,q[i].y);
		if(!q[i].op) hs[(ll)q[i].x*m+q[i].y]=i;//hash
		else if(q[i].op==1) q[hs[(ll)q[i].x*m+q[i].y]].tm=i;//hash
	}
	cnt=n;
	for(int i=0;i<=n;i++) s[i]=i,v[i]=M;
	for(int i=1;i<=m;i++) if(!q[i].op && !q[i].tm) q[i].tm=M;
	for(int i=1;i<=m;i++){
		if(!q[i].op){
			if(findroot(q[i].x)!=findroot(q[i].y)) add(q[i].x,q[i].y,q[i].tm);
			else{
				int p=query(q[i].x,q[i].y);
				if(v[p]<q[i].tm){cut(p,x[p]);cut(p,y[p]);add(q[i].x,q[i].y,q[i].tm);}
			}
		}
		else if(q[i].op==2) (findroot(q[i].x)!=findroot(q[i].y) || v[query(q[i].x,q[i].y)]<i)?puts("N"):puts("Y");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值