AtCoder-abc350_g 题解
题意简述
有一个无向图,初始时没有边。接下来有一些操作:
-
将 \(u,v\) 连边。
-
询问 \(u,v\) 的距离是否为 \(2\),如果是,则输出中间的那个点的编号,否则输出
0
。
每次询问后,更新 \(lastans\) 为询问的答案,初始时为 \(0\)。每次操作的 \(opt,u,v\) 使用 \(lastans\) 解码,假设给定的三个数为 \(a,b,c\):
\(opt=1+(((a \times (1+lastans)) \bmod 998244353) \bmod 2)\)
\(u=1+(((b \times (1+lastans)) \bmod 998244353) \bmod N)\)
\(v=1+(((c \times (1+lastans)) \bmod 998244353) \bmod N)\)
思路
本题可以用 LCT 做。
操作 \(1\),我们可以用 LCT 的 link
功能连边。
操作 \(2\),我们可以多维护一个子树大小,将 \(u-v\) 的路径 split
出来。先判断 \(v_{sz}\) 是否等于 \(3\),如果不是,则距离不为 \(2\)。否则有两种情况:
- \(v_{ls}=u\):
因为该点在 Splay 树中 \(u\) 的右边,\(v\) 的左边,因此 \(u_{rs}\) 即为该点。
- \(v_{ls} \ne u\)
此时 \(v_{ls}\) 即为中间点。同时一定满足 \(v_{ls_{ls}}=u\)。
这样这道题就做出来了。
代码
#include<bits/stdc++.h>
#define UP(i,a,b) for(i=a;i<=(b);++i)
#define DN(i,a,b) for(i=a;i>=(b);--i)
#define up(i,a,b) for(i=a;i<(b);++i)
#define dn(i,a,b) for(i=a;i>(b);--i)
#define pa make_pair
typedef long long ll;
using namespace std;
void rdc(char &c){for(c=getchar();c==' '||c=='\r'||c=='\n';c=getchar());}
const int N=1e5+5,mo=998244353;
int n,q;
ll lastans;
struct node{
bool lz;
int sz,vl;
node *ls,*rs,*fa;
}t[N],*root,*tot;
void init_node(node *u,int k){
u->lz=false;
u->sz=1;
u->vl=k;
u->ls=u->rs=u->fa=t;
}
void push_up(node *u){
u->sz=u->ls->sz+u->rs->sz+1;
}
bool ls(node *u){
return u->fa->ls==u;
}
bool isrt(node *u){
return u->fa->ls!=u&&u->fa->rs!=u;
}
void tag(node *u){
if(u!=t){
swap(u->ls,u->rs);
u->lz^=true;
}
}
void push_down(node *u){
if(u->lz){
tag(u->ls);
tag(u->rs);
u->lz=false;
}
}
void push(node *u){
if(!isrt(u)){
push(u->fa);
}
push_down(u);
}
void rtt(node *u){
node *f=u->fa,*g=f->fa;
if(ls(u)){
f->ls=u->rs;
if(f->ls!=t){
f->ls->fa=f;
}
u->rs=f;
}else{
f->rs=u->ls;
if(f->rs!=t){
f->rs->fa=f;
}
u->ls=f;
}
if(!isrt(f)){
ls(f)?g->ls=u:g->rs=u;
}
f->fa=u;u->fa=g;
push_up(f);push_up(u);
}
void splay(node *u){
/*将u旋转至链的根*/
node *f;
push(u);
while(!isrt(u)){
f=u->fa;
if(!isrt(f)){
rtt(ls(u)^ls(f)?u:f);
}
rtt(u);
}
}
void access(node *u){
/*将u与该树的根连实链*/
node *s=t;
while(u!=t){
splay(u);
u->rs=s;
push_up(u);
s=u;u=u->fa;
}
}
void mkrt(node *u){
/*将u设为原树中的根节点*/
access(u);
splay(u);
tag(u);
}
void split(node *u,node *v){
/*分裂u-v的路径*/
mkrt(u);
access(v);
splay(v);
}
void link(node *u,node *v){
/*连接路径u-v*/
mkrt(u);
u->fa=v;
}
node* fdrt(node *u){
/*查找u所在连通块的根*/
access(u);splay(u);
while(u->ls!=t){
u=u->ls;
push_down(u);
}
return u;
}
void init(){
int i;
UP(i,1,n){
init_node(t+i,i);
/*点i的权值为i*/
}
}
int main(){
int o,u,v;
scanf("%d%d",&n,&q);
init();
while(q--){
scanf("%d%d%d",&o,&u,&v);
o=1+(((o*(1+lastans))%mo)%2);
u=1+(((u*(1+lastans))%mo)%n);
v=1+(((v*(1+lastans))%mo)%n);
if(o==1){
link(t+u,t+v);
}else{
if(fdrt(t+u)!=fdrt(t+v)){
/*两点不连通*/
printf("%lld\n",lastans=0);
continue;
}
split(t+u,t+v);
if(t[v].sz==3){
/*有中间点*/
printf("%lld\n",lastans=(t[v].ls->vl==u?t[u].rs->vl:t[v].ls->vl));
/*如果v_ls=u,则答案为u_rs,否则为v_ls*/
}else{
/*路径长度不为3*/
printf("%lld\n",lastans=0);
}
}
}
return 0;
}