HDU6858 - 2020杭电多校第八场 1004 - Discovery of Cycles

本文介绍了一种处理大量在线询问的高效算法,用于判断无向图中特定边区间组成的子图是否存在环。通过预处理和使用LCT数据结构,避免了重复工作,实现了快速响应。适用于边数和询问次数较大的场景。

大致题意:

给定 nnn 个点 mmm 条边的无向图,边的编号为 111mmm ,现有 qqq 次在线询问,每次询问给出 l,r (1≤l≤r≤m)l,r~(1 \leq l \leq r \leq m)l,r (1lrm) ,问编号在 [l,r][l,r][l,r] 内的边组成的图中是否存在环;
1≤n,m,q≤3⋅1051 \leq n,m,q \leq 3 \cdot 10^51n,m,q3105

分析:

询问特别多,考虑预处理出以每一个边为起点边 形成环需要到达的最早的边,设为 ans[l]=rans[l]=rans[l]=r,表示第 lll 条边到第 rrr 条边构成的图中存在环;而判环比较简单的是并查集,但是根据数据范围,肯定不可能每一个起点都重新跑一遍并查集,会产生大量重复。
在起点边编号递增的情况下,对应的答案编号肯定也是非递减的;所以若我们可以选择删边,那么起点边编号每 +1+1+1 ,删掉前一条边,就可以节约一大部分重复的工作;
而考虑可以删边,且能提供类似并查集判环的数据结构,比较明显的就是 LCTLCTLCT 了;

代码:

#include<bits/stdc++.h>
#define RI register int
#define I inline
#define IV inline void
#define lc c[x][0]
#define rc c[x][1]
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N = 3E5+9;

int f[N],c[N][2],st[N];
bool r[N];

I bool noroot(RI x){ //判断节点是否为一个Splay的根 
	return c[f[x]][0]==x||c[f[x]][1]==x;
}  //如果非根,父亲的儿子里没有它 

IV pushup(RI x){ 
}

IV pushr(RI x){ //翻转操作 
	RI t=lc;lc=rc;rc=t;r[x]^=1;
} 

IV pushdown(RI x){ //判断并释放懒标记 
	if(r[x]){
		if(lc) pushr(lc);
		if(rc) pushr(rc);
		r[x]=0;
	}
}

IV rotate(RI x){ //一次旋转 
	RI y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
	if(noroot(y)) c[z][c[z][1]==y]=x;
	c[x][!k]=y;
	c[y][k]=w;
	if(w) f[w]=y;
	f[y]=x,f[x]=z;
	pushup(y);
} 

IV splay(RI x){
	RI y=x,z=0;
	st[++z]=y; //st为栈,暂存当前点到根的整条路径
	while(noroot(y)) st[++z]=y=f[y];
	while(z) pushdown(st[z--]);
	while(noroot(x)){
		y=f[x],z=f[y];
		if(noroot(y))
		    rotate((c[y][0]==x)^(c[z][0]==y)?x:y);
		rotate(x);
	} 
	pushup(x);
}

IV access(RI x){ //x-root拉一条实线 
	for(RI y=0;x;x=f[y=x])
	    splay(x),rc=y,pushup(x); 
}

IV makeroot(RI x){  //换根 
	access(x);
	splay(x);
	pushr(x); 
}

int findroot(RI x){ //找根(原树中) 
	access(x);splay(x);
	while(lc) pushdown(x),x=lc;
	splay(x);
	return x; 
}

IV split(RI x,RI y){ //提取x-y路径 
	makeroot(x);
	access(y);splay(y);
}

IV link(RI x,RI y){ //连边 
	makeroot(x);
	if(findroot(y)!=x)f[x]=y; 
} 

IV cut(RI x,RI y){
	makeroot(x);
	if(findroot(y)==x&&f[y]==x&&!c[y][0]){
		f[y]=c[x][1]=0;
		pushup(x);
	}
} 

int n,m,q,u[N],v[N],ans[N],lastans;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	
	int t; cin>>t;
	while(t--)
	{
		cin>>n>>m>>q;
		rep(i,0,n) r[i]=f[i]=c[i][0]=c[i][1]=0;
		rep(i,1,m) cin>>u[i]>>v[i],ans[i]=0;
		
		int cnt=1;
		rep(i,1,m){
			if(i>1) cut(u[i-1],v[i-1]);
			for(;cnt<=m;cnt++)
			{
				if(findroot(u[cnt])!=findroot(v[cnt])){
					link(u[cnt],v[cnt]);
				}
				else{
					ans[i]=cnt;
					break;
				}
			}
		}
		lastans=0;
		while(q--)
		{
			int _l,_r; cin>>_l>>_r;
			int k1=(_l^lastans)%m+1;
			int k2=(_r^lastans)%m+1;
			_l=min(k1,k2);
			_r=max(k1,k2);
			if(ans[_l]&&ans[_l]<=_r)
				cout<<"Yes\n",lastans=1;
			else 
			    cout<<"No\n",lastans=0;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值