大致题意:
给定 nnn 个点 mmm 条边的无向图,边的编号为 111 到 mmm ,现有 qqq 次在线询问,每次询问给出 l,r (1≤l≤r≤m)l,r~(1 \leq l \leq r \leq m)l,r (1≤l≤r≤m) ,问编号在 [l,r][l,r][l,r] 内的边组成的图中是否存在环;
1≤n,m,q≤3⋅1051 \leq n,m,q \leq 3 \cdot 10^51≤n,m,q≤3⋅105
分析:
询问特别多,考虑预处理出以每一个边为起点边 形成环需要到达的最早的边,设为 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;
}
}
}
本文介绍了一种处理大量在线询问的高效算法,用于判断无向图中特定边区间组成的子图是否存在环。通过预处理和使用LCT数据结构,避免了重复工作,实现了快速响应。适用于边数和询问次数较大的场景。
316

被折叠的 条评论
为什么被折叠?



