T(T<=10)组样例,
每次给出n(n<=3e5)个点,m(m<=3e5)条边的图,q(q<=3e5)个询问,
每次询问给出[l,r],询问只用第l,l+1,...,r条边构成的图,是否存在至少一个简单环
保证sumn<=1.5e6,summ<=1.5e6,询问强制在线
官方题解
Link-Cut Tree板子题,询问强制在线也没用,因为可以离线处理,然后O(1)回答
mn[i]:表示最小的边的编号,使[i,mn[i]]这一段的编号成环,
注意到随着i的单增,mn[i]是单调不减的,
反证法,如果mn[i+1]
所以,双指针,不断往动态图里加边,
直至要加的这条边的两个端点u,v在加这条边之前已经连通,就得到了mn[i]的答案
然后删去第i条边,再继续跑双指针往右移动
所以需要一个能动态删边、加边、判连通的数据结构,
于是就变成LCT的裸题了,槽,怪不得120队过……
#include
#define lc c[x][0]
#define rc c[x][1]
using namespace std;
const int N=3e5+9;
//f:父亲 c:儿子 v:单点值 s:子树值 stk:用于从上到下释放标记的栈 r:区间翻转标记
int t,n,m,q,mn[N],f[N],c[N][2],stk[N];
struct edge{
int u,v;
}e[N];
bool r[N];
bool nroot(int x){//判断节点是否为一个Splay的根(与普通Splay的区别1)
return c[f[x]][0]==x||c[f[x]][1]==x;
}//原理很简单,如果连的是轻边,他的父亲的儿子里没有它
void pushup(int x){//上传信息
//s[x]=s[lc]^s[rc]^v[x];
}
void pushr(int x){int t=lc;lc=rc;rc=t;r[x]^=1;}//翻转操作
void pushdown(int x){//判断并释放懒标记
if(r[x]){
if(lc)pushr(lc);
if(rc)pushr(rc);
r[x]=0;
}
}
void rotate(int x){//一次旋转
int y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
if(nroot(y))c[z][c[z][1]==y]=x;c[x][!k]=y;c[y][k]=w;//额外注意if(nroot(y))语句,此处不判断会引起致命错误(与普通Splay的区别2)
if(w)f[w]=y;f[y]=x;f[x]=z;
pushup(y);
}
void splay(int x){//只传了一个参数,因为所有操作的目标都是该Splay的根(与普通Splay的区别3)
int y=x,z=0;
stk[++z]=y;//st为栈,暂存当前点到根的整条路径,pushdown时一定要从上往下放标记(与普通Splay的区别4)
while(nroot(y))stk[++z]=y=f[y];
while(z)pushdown(stk[z--]);
while(nroot(x)){
y=f[x];z=f[y];
if(nroot(y))
rotate((c[y][0]==x)^(c[z][0]==y)?x:y);
rotate(x);
}
pushup(x);
}
void access(int x){//访问
for(int y=0;x;x=f[y=x]){
splay(x),rc=y,pushup(x);
}
}
void makeroot(int x){//换根
access(x);splay(x);
pushr(x);
}
int findroot(int x){//找根(在真实的树中的)
access(x);splay(x);
while(lc)pushdown(x),x=lc;
splay(x);
return x;
}
void split(int x,int y){//提取路径
makeroot(x);
access(y);splay(y);
}
bool connect(int x,int y){
makeroot(x);
return findroot(y)==x;
}
void link(int x,int y){//连边
makeroot(x);
if(findroot(y)!=x)f[x]=y;
}
void cut(int x,int y){//断边
makeroot(x);
if(findroot(y)==x&&f[y]==x&&!c[y][0]){
f[y]=c[x][1]=0;
pushup(x);
}
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;++i){
scanf("%d%d",&e[i].u,&e[i].v);
}
int now=1;
for(int i=1;i<=m;++i){
while(now<=m && !connect(e[now].u,e[now].v)){
link(e[now].u,e[now].v);
now++;
}
mn[i]=now;
cut(e[i].u,e[i].v);
}
int las=0,k1,k2,l,r;
while(q--){
scanf("%d%d",&l,&r);
k1=(l^las)%m+1;
k2=(r^las)%m+1;
l=min(k1,k2);
r=max(k1,k2);
if(mn[l]<=r){
las=1;
puts("Yes");
}
else{
las=0;
puts("No");
}
}
}
return 0;
}