【LOJ121】「离线可过」动态图连通性(线段树分治)(并查集)

传送门


题解:

每条边的出现是在一定时间范围内的。

于是我们考虑以时间为下标建立线段树,同时维护可回退化并查集就行了。

所谓的可回退化,也可以理解为可以还原到原来的某个版本,我们不使用路径压缩,而是按照 s i z siz siz合并,并且用一个栈来保存所有对并查集的修改操作。

然后需要的时候直接回退到历史版本就行了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		re char c;
		while(!isdigit(c=gc()));re T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int getint(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second

cs int N=5e3+3,M=5e5+5;

int n,m;

int *sta[N<<1],pre[N<<1],top;
inline void pack(int &x){
	sta[++top]=&x;
	pre[top]=x;
}

int fa[N],siz[N];
inline int getfa(int x){
	return x==fa[x]?x:getfa(fa[x]);
}

inline void merge(int u,int v){
	u=getfa(u),v=getfa(v);
	if(u==v)return ;
	if(siz[u]>siz[v])std::swap(u,v);
	pack(siz[v]);siz[v]+=siz[u];
	pack(fa[u]);fa[u]=v;
}

std::vector<int> E[M<<2];
std::map<pii,int> ma;
int ecnt,u[M],v[M];
bool ins[M],qe[M];

inline void modify(int k,int l,int r,cs int &ql,cs int &qr,cs int &id){
	if(ql<=l&&r<=qr)return E[k].push_back(id);
	int mid=(l+r)>>1;
	if(ql<=mid)modify(k<<1,l,mid,ql,qr,id);
	if(mid<qr)modify(k<<1|1,mid+1,r,ql,qr,id);
}

inline void solve(int k,int l,int r){
	int low=top;
	for(int re e:E[k])merge(u[e],v[e]);
	if(l==r){
		if(qe[l])puts(getfa(u[l])==getfa(v[l])?"Y":"N");
		while(top>low){*sta[top]=pre[top];--top;}
		return ;
	}
	int mid=(l+r)>>1;
	solve(k<<1,l,mid);
	solve(k<<1|1,mid+1,r);
	while(top>low){*sta[top]=pre[top];--top;}
}

signed main(){
//	freopen("graph.in","r",stdin);
	n=getint(),m=getint();
	for(int re i=1;i<=m;++i)switch(getint()){
		case 0:{
			u[i]=getint(),v[i]=getint();
			if(u[i]>v[i])std::swap(u[i],v[i]);
			ma[pii(u[i],v[i])]=i;
			break;
		}
		case 1:{
			u[i]=getint(),v[i]=getint();
			if(u[i]>v[i])std::swap(u[i],v[i]);
			int id=ma[pii(u[i],v[i])];
			ins[id]=ins[i]=true;
			modify(1,1,m,id,i,id);
			break;
		}
		case 2:{
			qe[i]=true;
			u[i]=getint(),v[i]=getint();
			break;
		}
	}
	for(int re i=1;i<=n;++i)fa[i]=i,siz[i]=1;
	for(int re i=1;i<=m;++i)if(!ins[i]&&!qe[i])modify(1,1,m,i,m,i);
	solve(1,1,m);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值