AtCoder-abc350_g 题解

原题链接

题意简述

有一个无向图,初始时没有边。接下来有一些操作:

  1. \(u,v\) 连边。

  2. 询问 \(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\)。否则有两种情况:

  1. \(v_{ls}=u\)

因为该点在 Splay 树中 \(u\) 的右边,\(v\) 的左边,因此 \(u_{rs}\) 即为该点。

  1. \(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;
}
posted @ 2024-04-29 21:09  lrx139  阅读(22)  评论(0编辑  收藏  举报