题解 P5622 【[DBOI2019]巫女的职责】
Brodal_Queue · · 题解
emmm 这里是萌新的第一篇题解
首先想想没有加边怎么做:
由于维护的是必经点,也就是割点的信息。
很容易想到构建圆方树,对于每个点双连通分量建一个方点,点双中的点都连过去,其它边都去掉。
既然还有动态加边,首先当然是用
对于
主要说一下
首先两点不连通,当然是直接连上去;如果已连通,就把路径上的边都断掉,所有点都连向一个方点。
这样看起来非常暴力,但实际上时间复杂度是对的:用势能分析容易证明,复杂度为
ps:有一个小优化,就是当一条路径上除了两端全是方点时,就直接忽略连边操作,对答案没有影响。
代码如下:
#pragma GCC optimize (2)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 1000003
#define ll long long
using namespace std;
ll sum[N],a[N];
int son[N][2],fa[N],size[N],st[N];
bool tag[N],rev[N],real[N];
inline void pushup(int u){
size[u] = size[son[u][0]]+size[son[u][1]]+real[u];
sum[u] = sum[son[u][0]]+sum[son[u][1]]+a[u];
}
inline bool notrt(int u){
return son[fa[u]][0]==u||son[fa[u]][1]==u;
}
inline void push_tag(int u){
sum[u] = a[u] = 0;
tag[u] = true;
}
inline void push_rev(int u){
swap(son[u][0],son[u][1]);
rev[u] ^= 1;
}
inline void pushdown(int u){
if(tag[u]){
if(son[u][0]) push_tag(son[u][0]);
if(son[u][1]) push_tag(son[u][1]);
tag[u] = 0;
}
if(rev[u]){
if(son[u][0]) push_rev(son[u][0]);
if(son[u][1]) push_rev(son[u][1]);
rev[u] = 0;
}
}
inline bool check(int u){
return son[fa[u]][1]==u;
}
inline void rotate(int x){
int y = fa[x],z = fa[y];
int k = son[y][1]==x,w = son[x][k^1];
if(notrt(y)) son[z][check(y)] = x;
son[x][k^1] = y;
son[y][k] = w;
if(w) fa[w] = y;
fa[y] = x,fa[x] = z;
pushup(y);
}
inline void splay(int x){
int y = x,z = 1;
st[1] = y;
while(notrt(y)) st[++z] = y = fa[y];
while(z) pushdown(st[z--]);
while(notrt(x)){
y = fa[x],z = fa[y];
if(notrt(y)) rotate(check(x)==check(y)?y:x);
rotate(x);
}
pushup(x);
}
inline void access(int u){
for(int v=0;u;u=fa[v=u])
splay(u),son[u][1] = v,pushup(u);
}
inline void makeroot(int u){
access(u),splay(u);
push_rev(u);
}
inline void link(int u,int v){
makeroot(u);
fa[u] = v;
}
inline void split(int u,int v){
makeroot(u);
access(v),splay(v);
}
inline void cut(int u,int v){
split(u,v);
son[v][0] = fa[u] = 0;
pushup(v);
}
inline ll clear(int u,int v){
split(u,v);
ll res = sum[v];
push_tag(v);
return res;
}
int n,q,top,qaq;
ll ans;
int fk[N],stk[N];
inline int find(int x){
while(x!=fk[x]) x = fk[x] = fk[fk[x]];
return x;
}
inline void fuckyou(ll &x){
x ^= ans%n;
if(x>n) x %= n;
if(!x) x = 1;
}
void dfs(int u){
if(!u) return;
pushdown(u);
dfs(son[u][0]);
stk[++top] = u;
dfs(son[u][1]);
}
int main(){
int op;
ll x,y;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i){
fk[i] = i;
real[i] = true;
}
qaq = n;
while(q--){
scanf("%d%lld%lld",&op,&x,&y);
fuckyou(x),fuckyou(y);
if(op==1){
if(find(x)==find(y)){
split(x,y);
if(size[y]<=2) continue;
top = 0;
dfs(y);
++qaq;
for(int i=1;i<top;++i) cut(stk[i],stk[i+1]);
for(int i=1;i<=top;++i) link(stk[i],qaq);
}else{
link(x,y);
fk[find(x)] = find(y);
}
}else if(op==2){
splay(x);
a[x] += y;
}else{
ans = find(x)==find(y)?clear(x,y):0ll;
printf("%lld\n",ans);
}
}
}
因为之前有一道 P5489 所以这题就显得比较模板(