1 条题解
-
0
自动搬运
来自洛谷,原作者为

Brodal_Queue
EI 天下第一可爱!搬运于
2025-08-24 22:12:40,当前版本为作者最后更新于2019-11-04 20:21:12,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
emmm 这里是萌新的第一篇题解
首先想想没有加边怎么做:
由于维护的是必经点,也就是割点的信息。
很容易想到构建圆方树,对于每个点双连通分量建一个方点,点双中的点都连过去,其它边都去掉。既然还有动态加边,首先当然是用 啦。
对于 操作,就直接把 点 splay 上去,直接修改点权;对于 操作,就是路径求和 + 路径权值变 ,也没什么好说的。
主要说一下 操作的做法:
首先两点不连通,当然是直接连上去;如果已连通,就把路径上的边都断掉,所有点都连向一个方点。
这样看起来非常暴力,但实际上时间复杂度是对的:用势能分析容易证明,复杂度为 。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 所以这题就显得比较模板(
- 1
信息
- ID
- 4499
- 时间
- 1500ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者