title: LCT动态树
date: 2022-05-11 06:17:52
author: “胡耀文”
categories: “算法”
tags:
- “数据结构”
- “动态树”
- “LCT”
LCT
模板
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct node{
int s[2],p,v;
int sum,rev;
#define ls tr[u].s[0]
#define lx tr[x].s[0]
#define rs tr[u].s[1]
#define rx tr[x].s[1]
}tr[N];
int n,m,op,x,y;
bool inline isroot(int u){
return tr[tr[u].p].s[0]!=u&&tr[tr[u].p].s[1]!=u;
}
void inline pushup(int u){
tr[u].sum=tr[ls].sum^tr[u].v^tr[rs].sum;
}
void inline pushrev(int u){
swap(ls,rs);
tr[u].rev^=1;
}
void inline pushdown(int u){
if(tr[u].rev){
pushrev(ls),pushrev(rs);
tr[u].rev=0;
}
}
void inline rotate(int x){
int y=tr[x].p,z=tr[y].p;
int k=tr[y].s[1]==x;
if(!isroot(y))tr[z].s[tr[z].s[1]==y]=x;
tr[x].p=z;
tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y,tr[y].p=x;
pushup(y),pushup(x);
}
void inline splay(int x){
static int stk[N];
int tt=0,t=x;
stk[++tt]=t;
while(!isroot(t))stk[++tt]=t=tr[t].p;
while(tt)pushdown(stk[tt--]);
while(!isroot(x)){
int y=tr[x].p,z=tr[y].p;
if(!isroot(y)){
if( (tr[z].s[1]==y)^(tr[y].s[1]==x) )rotate(x);
else rotate(y);
}rotate(x);
}
}
void inline access(int x){
int z=x;
for(int y=0;x;y=x,x=tr[x].p){
splay(x);
rx=y;
pushup(x);
}
splay(z);
}
void inline makeroot(int x){
access(x);
pushrev(x);
}
int inline findroot(int u){
access(u);
while(ls)pushdown(u),u=ls;
splay(u);
return u;
}
void inline split(int x,int y){
makeroot(x);
access(y);
}
void inline link(int x,int y){
makeroot(x);
if(findroot(y)!=x)tr[x].p=y;
}
void inline cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&rx==y&&!tr[y].s[0]){
tr[y].p=rx=0;
pushup(x);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>tr[i].v;
while(m--){
cin>>op>>x>>y;
if(op==0){
split(x,y);
cout<<tr[y].sum<<'\n';
}
if(op==1)link(x,y);
if(op==2)cut(x,y);
if(op==3){
splay(x);
tr[x].v=y;
pushup(x);
}
}
}
化边权为点权+动态维护区间最大值编号+用编号删边
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m,p[N],stk[N];
int find(int x){
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
struct Edge{
int x,y,a,b;
bool operator<(const Edge&t)const{
return a<t.a;
}
}e[N];
struct node{
int s[2],p,v;
int mx,rev;
#define ls tr[u].s[0]
#define rs tr[u].s[1]
#define lx tr[x].s[0]
#define rx tr[x].s[1]
}tr[N];
void pushrev(int u){
swap(ls,rs);
tr[u].rev^=1;
}
void pushup(int u){
tr[u].mx=u;
int lmaxid=tr[ls].mx;
int rmaxid=tr[rs].mx;
if(tr[lmaxid].v>tr[tr[u].mx].v)tr[u].mx=lmaxid;
if(tr[rmaxid].v>tr[tr[u].mx].v)tr[u].mx=rmaxid;
}
void pushdown(int u){
if(tr[u].rev){
pushrev(ls);
pushrev(rs);
tr[u].rev=0;
}
}
bool isroot(int u){
return tr[tr[u].p].s[0]!=u&&tr[tr[u].p].s[1]!=u;
}
void rotate(int x){
int y=tr[x].p,z=tr[y].p;
int k=tr[y].s[1]==x;
if(!isroot(y))tr[z].s[tr[z].s[1]==y]=x;
tr[x].p=z;
tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y,tr[y].p=x;
pushup(y),pushup(x);
}
void splay(int x){
int top=0,r=x;
stk[++top]=r;
while(!isroot(r))stk[++top]=r=tr[r].p;
while(top)pushdown(stk[top--]);
while(!isroot(x)){
int y=tr[x].p,z=tr[y].p;
if(!isroot(y)){
if((tr[z].s[1]==y)^(tr[y].s[1]==x))rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int z=x;
for(int y=0;x;y=x,x=tr[y].p){
splay(x);
tr[x].s[1]=y,pushup(x);
}
splay(z);
}
void makeroot(int x){
access(x);
pushrev(x);
}
int findroot(int x){
access(x);
while(lx)pushdown(x),x=lx;
splay(x);
return x;
}
void split(int x,int y){
makeroot(x);
access(y);
}
void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)tr[x].p=y;
}
void cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&rx==y&&!tr[y].s[0]){
tr[y].p=rx=0;
pushup(x);
}
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,a,b;
cin>>x>>y>>a>>b;
e[i]={
x,y,a,b};
}
sort(e+1,e+1+m);
for(int i=1;i<=n+m;i++){
p[i]=i;
if(i>n)tr[i].v=e[i-n].b;
tr[i].mx=i;
}
int ans=1e9;
for(int i=1;i<=m;i++){
int x=e[i].x,y=e[i].y,a=e[i].a,b=e[i].b;
if(find(x)==find(y)){
split(x,y);
int t=tr[y].mx;
if(tr[t].v>b){
cut(t,e[t-n].x),cut(t,e[t-n].y);
link(i+n,x),link(i+n,y);
}
}
else{
p[find(x)]=find(y);
link(i+n,x),link(i+n,y);
}
if(find(1)==find(n)){
split(1,n);
ans=min(ans,a+tr[tr[n].mx].v);
}
}
cout<<(ans==1e9?-1:ans);
}
lct求子树大小
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<algorithm>
using namespace std;
struct node{
int fa,son[2],siz,exsiz,rev;
}t[100050];
inline void update(int x)
{
t[x].siz=t[t[x].son[0]].siz+t[t[x].son[1]].siz+t[x].exsiz+1;
}
inline void pushdown(int x)
{
if(t[x].rev)
{
swap(t[x].son[0],t[x].son[1]);
if(t[x].son[0]) t[t[x].son[0]].rev^=1;
if(t[x].son[1]) t[t[x].son[1]].rev^=1;
t[x].rev=0;
}
}
inline bool isroot(int x)
{
return t[t[x].fa].son[0]!=x&&t[t[x].fa].son[1]!=x;
}
inline int get(int x)
{
return t[t[x].fa].son[1]==x;
}
inline void rotate(int x)
{
int f=t[x].fa;int ff=t[f].fa;int gx=get(x);int gx2=get(f);
if(!isroot(f))
{
t[ff].son[gx2]=x;
}
t[f].son[gx]=t[x].son[gx^1];
t[t[x].son[gx^1]].fa=f;
t[x].son[gx^1]=f;
t[f].fa=x;
t[x].fa=ff;
update(f);
update(x);
}
int st[100050];
inline void splay(int x)
{
int top=0;
int y=x;
st[++top]=y;
while(!isroot(y))
{
y=t[y].fa;
st[++top]=y;
}
while(top)
{
pushdown(st[top--]);
}
while(!isroot(x))
{
int f=t[x].fa;
if(isroot(f))
{
rotate(x);
}
else if(get(x)==get(f))
{
rotate(f);
rotate(x);
}
else
{
rotate(x);
rotate(x);
}
update(x);
}
}
inline void access(int x)
{
for(int y=0;x;y=x,x=t[x].fa)
{
splay(x);
t[x].exsiz+=t[t[x].son[1]].siz-t[y].siz;
t[x].son[1]=y;
update(x);
}
}
inline void makeroot(int x)
{
access(x);
splay(x);
t[x].rev^=1;
pushdown(x);
}
inline void link(int x,int y)
{
makeroot(x);
makeroot(y);
t[x].fa=y;
t[y].exsiz+=t[x].siz;
update(y);
}
inline void split(int x,int y)
{
makeroot(x);
access(y);
splay(y);
}
int n,q;
int main()
{
scanf("%d%d",&n,&q);
while(q--)
{
char typ;
cin>>typ;
if(typ=='A')
{
int x,y;
scanf("%d%d",&x,&y);
link(x,y);
}
else
{
int x,y;
scanf("%d%d",&x,&y);
split(x,y);
t[y].son[0]=t[x].fa=0;
update(y);
makeroot(x);
makeroot(y);
long long ans=(long long)(t[x].siz)*(long long)(t[y].siz);
printf("%lld\n",ans);
t[x].fa=y;
t[y].exsiz+=t[x].siz;
}
}
return 0;
}
弹飞绵羊
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
struct node{
int s[2],p,v;
int sum,rev;
#define ls tr[u].s[0]
#define lx tr[x].s[0]
#define rs tr[u].s[1]
#define rx tr[x].s[1]
}tr[N];
int n,m,op,x,y;
bool inline isroot(int u){
return tr[tr[u].p].s[0]!=u&&tr[tr[u].p].s[1]!=u;
}
void inline pushup(int u){
tr[u].sum=tr[ls].sum+tr[u].v+tr[rs].sum;
}
void inline pushrev(int u){
swap(ls,rs);
tr[u].rev^=1;
}
void inline pushdown(int u){
if(tr[u].rev){
pushrev(ls),pushrev(rs);
tr[u].rev=0;
}
}
void inline rotate(int x){
int y=tr[x].p,z=tr[y].p;
int k=tr[y].s[1]==x;
if(!isroot(y))tr[z].s[tr[z].s[1]==y]=x;
tr[x].p=z;
tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y,tr[y].p=x;
pushup(y),pushup(x);
}
void inline splay(int x){
static int stk[N];
int tt=0,t=x;
stk[++tt]=t;
while(!isroot(t))stk[++tt]=t=tr[t].p;
while(tt)pushdown(stk[tt--]);
while(!isroot(x)){
int y=tr[x].p,z=tr[y].p;
if(!isroot(y)){
if( (tr[z].s[1]==y)^(tr[y].s[1]==x) )rotate(x);
else rotate(y);
}rotate(x);
}
}
void inline access(int x){
int z=x;
for(int y=0;x;y=x,x=tr[x].p){
splay(x);
rx=y;
pushup(x);
}
splay(z);
}
void inline makeroot(int x){
access(x);
pushrev(x);
}
int inline findroot(int u){
access(u);
while(ls)pushdown(u),u=ls;
splay(u);
return u;
}
void inline split(int x,int y){
makeroot(x);
access(y);
}
void inline link(int x,int y){
makeroot(x);
if(findroot(y)!=x)tr[x].p=y;
}
void inline cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&rx==y&&!tr[y].s[0]){
tr[y].p=rx=0;
pushup(x);
}
}
int fa[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
// cin>>n>>m;
// for(int i=1;i<=n;i++)cin>>tr[i].v;
// while(m--){
// cin>>op>>x>>y;
// if(op==0){
// split(x,y);
// cout<<tr[y].sum<<'\n';
// }
// if(op==1)link(x,y);
// if(op==2)cut(x,y);
// if(op==3){
// splay(x);
// tr[x].v=y;
// pushup(x);
// }
// }
cin>>n;
int T=n+1;
for(int i=1;i<=n+1;i++)tr[i].v=1;
for(int i=1;i<=n;i++){
cin>>fa[i];
fa[i]=min(fa[i]+i,n+1);
link(i,fa[i]);
}
cin>>m;
while(m--){
int op;
cin>>op;
if(op==1){
int x;
cin>>x;
x++;
split(x,T);
cout<<tr[T].sum-1<<'\n';
}
else{
int x,f;
cin>>x>>f;
x++;
cut(x,fa[x]);
f=min(T,f+x);
fa[x]=f;
link(x,f);
}
}
}
树上求割边割点数量
#include <cstdio>
#define N 200010
#define lc(x) ch[x][0]
#define rc(x) ch[x][1]
inline void swap(int&a,int&b) {
int tmp(a); a=b,b=tmp;
}
namespace Summer {
int ch[N][2],fa[N],rev[N],val[N],sumv[N],mark[N];
inline void reverse(int x) {
if(x) {
swap(lc(x),rc(x));
rev[x]^=1;
}
}
inline void NaCly_Fish_Orz(int x) {
if(x) {
val[x]=sumv[x]=0;
mark[x]=1;
}
}
inline void up(int x) {
sumv[x]=sumv[lc(x)]+sumv[rc(x)]+val[x];
}
inline void down(int x) {
if(rev[x]) {
reverse(lc(x));
reverse(rc(x));
rev[x]=0;
}
if(mark[x]) {
NaCly_Fish_Orz(lc(x));
NaCly_Fish_Orz(rc(x));
mark[x]=0;
}
}
inline int nrt(int x) {
return x==lc(fa[x])||x==rc(fa[x]);
}
void psa(int x) {
if(nrt(x)) psa(fa[x]);
down(x);
}
inline void rotate(int x) {
int y(fa[x]),z(fa[y]),k(x==rc(y));
ch[y][k]=ch[x][k^1],ch[x][k^1]=y;
if(nrt(y)) ch[z][y==rc(z)]=x;
if(ch[y][k]) fa[ch[y][k]]=y;
fa[y]=x,fa[x]=z,up(y);
}
inline void splay(int x) {
int y,z;
for(psa(x);nrt(x);rotate(x)) {
y=fa[x],z=fa[y];
if(nrt(y)) rotate(x==rc(y)^y==rc(z)?x:y);
} up(x);
}
inline void access(int x) {
for(int y=0;x;x=fa[y=x]) {
splay(x); rc(x)=y; up(x);
}
}
inline void mrt(int x) {
access(x); splay(x); reverse(x);
}
inline void link(int x,int y) {
mrt(x); fa[x]=y;
}
inline void cut(int x,int y) {
mrt(x); access(y); splay(y);
fa[x]=lc(y)=0; up(y);
}
}
namespace Pockets {
int ch[N][2],fa[N],rev[N],val[N],sumv[N],st[N],num;
inline void reverse(int x) {
if(x) {
swap(lc(x),rc(x));
rev[x]^=1;
}
}
inline void up(int x) {
sumv[x]=sumv[lc(x)]+sumv[rc(x)]+val[x];
}
inline void down(int x) {
if(rev[x]) {
reverse(lc(x));
reverse(rc(x));
rev[x]=0;
}
}
inline int nrt(int x) {
return x==lc(fa[x])||x==rc(fa[x]);
}
void psa(int x) {
if(nrt(x)) psa(fa[x]);
down(x);
}
inline void rotate(int x) {
int y(fa[x]),z(fa[y]),k(x==rc(y));
ch[y][k]=ch[x][k^1],ch[x][k^1]=y;
if(nrt(y)) ch[z][y==rc(z)]=x;
if(ch[y][k]) fa[ch[y][k]]=y;
fa[y]=x,fa[x]=z,up(y);
}
inline void splay(int x) {
int y,z;
for(psa(x);nrt(x);rotate(x)) {
y=fa[x],z=fa[y];
if(nrt(y)) rotate(x==rc(y)^y==rc(z)?x:y);
} up(x);
}
inline void access(int x) {
for(int y=0;x;x=fa[y=x]) {
splay(x); rc(x)=y; up(x);
}
}
inline void mrt(int x) {
access(x); splay(x); reverse(x);
}
inline void link(int x,int y) {
mrt(x); fa[x]=y;
}
inline void cut(int x,int y) {
mrt(x); access(y); splay(y);
fa[x]=lc(y)=0; up(y);
}
void print(int now) {
if(now) {
down(now);
print(lc(now));
st[++num]=now;
print(rc(now));
}
}
}
int n,q,opt,u,v,last,tot,ans,SummerPockets;
int fa[N];
inline int find(int x) {
return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline void getEdge(int u,int v) {
int x=find(u),y=find(v);
if(x!=y) {
ans=-1;
return;
}
Summer::mrt(u);
Summer::access(v);
Summer::splay(v);
ans=Summer::sumv[v];
}
inline void getPoint(int u,int v) {
int x=find(u),y=find(v);
if(x!=y) {
ans=-1;
return;
}
Pockets::mrt(u);
Pockets::access(v);
Pockets::splay(v);
ans=Pockets::sumv[v];
}
inline void link(int u,int v) {
int x=find(u),y=find(v);
if(x==y) {
Summer::mrt(u);
Summer::access(v);
Summer::splay(v);
Summer::NaCly_Fish_Orz(v);
getPoint(u,v);
if(ans>2) {
++SummerPockets;
Pockets::mrt(u);
Pockets::access(v);
Pockets::splay(v);
Pockets::num=0;
Pockets::print(v);
for(int i=1;i<Pockets::num;++i)
Pockets::cut(Pockets::st[i],Pockets::st[i+1]);
for(int i=1;i<=Pockets::num;++i)
Pockets::link(Pockets::st[i],SummerPockets);
}
} else {
++tot; fa[y]=x;
Summer::val[tot]=1;
Summer::link(u,tot);
Summer::link(tot,v);
Pockets::link(u,v);
}
}
int main() {
scanf("%d%d",&n,&q);
tot=SummerPockets=n;
for(int i=1;i<=n;++i)
fa[i]=i,Pockets::val[i]=1;
while(q--) {
scanf("%d%d%d",&opt,&u,&v);
u^=last,v^=last;
switch(opt) {
case 1: {
link(u,v);
break;
}
case 2: {
getEdge(u,v);
if(ans!=-1) last=ans;
printf("%d\n",ans);
break;
}
default: {
getPoint(u,v);
if(ans!=-1) last=ans;
printf("%d\n",ans);
break;
}
}
}
return 0;
}
LCT+LCA
1操作很容易联想到LCT的access,考虑对于剩下俩操作的维护,但其实对于树上点到根的路径权值具有可减性,于是维护这样一个距离,然后用树上差分回答2询问,因为一个点的子树在dfs序上是连续的一段,可以用线段树维护最大值回答3询问
这个题就成了3个板子的嵌套+巧妙的LCT,线段树和lca都是基础了不必再赘述
巧妙的LCT在于可以用这个题加深对access的理解,单独拿出access来说
void access(int x) {
for (int y = 0; x; y = x, x = fa[x]) {
splay(x); rs = y; //pushup(x);
}
}
void access(int x) {
int c;
for (int y = 0; x; y = x, x = fa[x]) {
splay(x);
if (rs) c = findrt(rs), SEG::change(1, 1, n, dfn[c], dfn[c] + siz[c] - 1, 1);
if (rs = y) c = findrt(y), SEG::change(1, 1, n, dfn[c], dfn[c] + siz[c] - 1, -1);
}
}
access实质是我们要对于x到根路径全部变成实边,我们要的是对于维护的信息进行修改
考虑性质,虚边有贡献,而实边没有贡献,在具体实现过程中,对于点x,遇到它的父亲到它为虚边则变实,然后整个子树到根节点的距离-1,然后把x的父亲原来连的实边的那个子树贡献+1,即可解决1操作
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#define q(x, y) SEG::query(1, 1, n, x, y)
#define inf 1e9
using namespace std;
void file() {
freopen("read.in", "r", stdin);
freopen("write.out", "w", stdout);
}
const int N = 1e6 + 10;
inline int read() {
int sym = 0, res = 0; char ch = getchar();
while (!isdigit(ch)) sym |= (ch == '-'), ch = getchar();
while (isdigit(ch)) res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return sym ? -res : res;
}
struct EDGE {
int u, v, nxt;
} edge[N];
int n, m, head[N], cnt, dfn[N], siz[N], depth[N], rev[N];
void add(int u, int v) {
edge[++cnt] = (EDGE){
u, v, head[u]}; head[u] = cnt;}
namespace SEG {
int tmax[N], tag[N];
#define ls x << 1
#define rs x << 1 | 1
void pushup(int x) {
tmax[x] = max(tmax[ls], tmax[rs]);}
void update(int x, int t) {
tmax[x] += t; tag[x] += t;}
void pushdown(int x, int l, int r, int mid) {
if (tag[x]) {
update(ls, tag[x]); update(rs, tag[x]); tag[x] = 0;
}
}
void build(int x, int l, int r) {
if (l == r) {
tmax[x] = depth[rev[l]]; return;}
int mid = l + r >> 1;
build(ls, l, mid); build(rs, mid + 1, r);
pushup(x);
}
void change(int x, int l, int r, int ln, int rn, int t) {
if (ln <= l && r <= rn) {
update(x, t); return;}
int mid = l + r >> 1; pushdown(x, l, r, mid);
if (mid >= ln) change(ls, l, mid, ln, rn, t);
if (mid + 1 <= rn) change(rs, mid + 1, r, ln, rn, t);
pushup(x);
}
int query(int x, int l, int r, int ln, int rn) {
if (ln <= l && r <= rn) return tmax[x];
int mid = l + r >> 1, res = 0; pushdown(x, l, r, mid);
if (mid >= ln) res = max(res, query(ls, l, mid, ln, rn));
if (mid + 1 <= rn) res = max(res, query(rs, mid + 1, r, ln, rn));
return res;
}
}
namespace LCT {
int son[N][2], fa[N];
#define ls son[x][0]
#define rs son[x][1]
bool l_r(int x) {
return x == son[fa[x]][1];}
bool isroot(int x) {
return son[fa[x]][0] != x && son[fa[x]][1] != x;}
int findrt(int x) {
while (ls) x = ls; return x;}
void rotate(int x) {
int y = fa[x], z = fa[y], kind = l_r(x);
if (!isroot(y)) son[z][l_r(y)] = x;
son[y][kind] = son[x][kind ^ 1]; fa[son[x][kind ^ 1]] = y;
son[x][kind ^ 1] = y; fa[y] = x; fa[x] = z;
}
void splay(int x) {
for (int y = fa[x]; !isroot(x); y = fa[x]) {
if (!isroot(y)) rotate(l_r(x) == l_r(y) ? y : x); rotate(x);
}
}
void access(int x) {
int c;
for (int y = 0; x; y = x, x = fa[x]) {
splay(x);
if (rs) c = findrt(rs), SEG::change(1, 1, n, dfn[c], dfn[c] + siz[c] - 1, 1);
if (rs = y) c = findrt(y), SEG::change(1, 1, n, dfn[c], dfn[c] + siz[c] - 1, -1);
}
}
}
namespace LCA {
int top[N], son[N], fa[N];
void dfs1(int u, int last) {
depth[u] = depth[last] + 1; siz[u] = 1; fa[u] = last;
for (int e = head[u]; e; e = edge[e].nxt) {
int v = edge[e].v; if (v == last) continue; dfs1(v, u);
siz[u] += siz[v]; if (siz[v] > siz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int front) {
dfn[u] = ++cnt; rev[cnt] = u; top[u] = front; if (son[u]) dfs2(son[u], front);
for (int e = head[u]; e; e = edge[e].nxt) {
int v = edge[e].v; if (v == fa[u] || v == son[u]) continue; dfs2(v, v);
}
}
int lca(int x, int y) {
while (top[x] != top[y]) depth[top[x]] < depth[top[y]] ? y = fa[top[y]] : x = fa[top[x]];
return depth[x] < depth[y] ? x : y;
}
}
int main() {
n = read(); m = read();
for (int i = 1; i < n; i++) {
int u = read(), v = read(); add(u, v); add(v, u);
}
cnt = 0; LCA::dfs1(1, 0); LCA::dfs2(1, 1); cnt = 0; SEG::build(1, 1, n);
for (int i = 1; i <= n; i++) LCT::fa[i] = LCA::fa[i];
for (int i = 1; i <= m; i++) {
int opt = read(), x = read();
if (opt == 1) LCT::access(x);
if (opt == 2) {
int y = read(), lca = LCA::lca(x, y);
printf("%d\n", q(dfn[x], dfn[x]) + q(dfn[y], dfn[y]) - 2 * q(dfn[lca], dfn[lca]) + 1);
}
if (opt == 3) {
printf("%d\n", q(dfn[x], dfn[x] + siz[x] - 1));
}
}
return 0;
}
LCT懒标记
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
const int mod=51061;
struct node{
int s[2],p,v;
int sum,rev,mul,add,sz;
#define ls tr[u].s[0]
#define lx tr[x].s[0]
#define rs tr[u].s[1]
#define rx tr[x].s[1]
}tr[N];
int n,m,op,x,y;
bool inline isroot(int u){
return tr[tr[u].p].s[0]!=u&&tr[tr[u].p].s[1]!=u;
}
void inline pushup(int u){
tr[u].sum=(tr[ls].sum+tr[u].v+tr[rs].sum)%mod;
tr[u].sz=tr[ls].sz+tr[rs].sz+1;
}
void inline pushrev(int u){
swap(ls,rs);
tr[u].rev^=1;
}
void pushmul(int u,int v){
tr[u].sum=tr[u].sum*v%mod;
tr[u].v=tr[u].v*v%mod;
tr[u].add=tr[u].add*v%mod;
tr[u].mul=tr[u].mul*v%mod;
}
void pushadd(int u,int v){
tr[u].sum=(tr[u].sum+tr[u].sz*v)%mod;
tr[u].v=(tr[u].v+v)%mod;
tr[u].add=(tr[u].add+v)%mod;
}
void inline pushdown(int u){
if(tr[u].rev){
pushrev(ls),pushrev(rs);
tr[u].rev=0;
}
if(tr[u].mul!=1){
pushmul(ls,tr[u].mul),pushmul(rs,tr[u].mul);
tr[u].mul=1;
}
if(tr[u].add!=0){
pushadd(ls,tr[u].add),pushadd(rs,tr[u].add);
tr[u].add=0;
}
}
void inline rotate(int x){
int y=tr[x].p,z=tr[y].p;
int k=tr[y].s[1]==x;
if(!isroot(y))tr[z].s[tr[z].s[1]==y]=x;
tr[x].p=z;
tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y,tr[y].p=x;
pushup(y),pushup(x);
}
void inline splay(int x){
static int stk[N];
int tt=0,t=x;
stk[++tt]=t;
while(!isroot(t))stk[++tt]=t=tr[t].p;
while(tt)pushdown(stk[tt--]);
while(!isroot(x)){
int y=tr[x].p,z=tr[y].p;
if(!isroot(y)){
if( (tr[z].s[1]==y)^(tr[y].s[1]==x) )rotate(x);
else rotate(y);
}rotate(x);
}
}
void inline access(int x){
int z=x;
for(int y=0;x;y=x,x=tr[x].p){
splay(x);
rx=y;
pushup(x);
}
splay(z);
}
void inline makeroot(int x){
access(x);
pushrev(x);
}
int inline findroot(int u){
access(u);
while(ls)pushdown(u),u=ls;
splay(u);
return u;
}
void inline split(int x,int y){
makeroot(x);
access(y);
}
void inline link(int x,int y){
makeroot(x);
if(findroot(y)!=x)tr[x].p=y;
}
void inline cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&rx==y&&!tr[y].s[0]){
tr[y].p=rx=0;
pushup(x);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)tr[i].mul=tr[i].v=tr[i].sz=tr[i].sum=1;
for(int i=1;i<n;i++){
cin>>x>>y;
link(x,y);
}
while(m--){
char c;
int v;
cin>>c;
if(c=='+'){
cin>>x>>y>>v;
split(x,y);
pushadd(y,v);
}
if(c=='-'){
cin>>x>>y;cut(x,y);
cin>>x>>y;link(x,y);
}
if(c=='*'){
cin>>x>>y>>v;
split(x,y);
pushmul(y,v);
}
if(c=='/'){
cin>>x>>y;
split(x,y);
cout<<tr[y].sum<<'\n';
}
}
}
用动态树解决树链剖分问题
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
#define int long long
int n,m,p[N],stk[N];
int find(int x){
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
struct Edge{
int x,y,a,b;
bool operator<(const Edge&t)const{
return a<t.a;
}
}e[N];
struct node{
int s[2],p,v;
int max,min,sum,rev,lazy;
#define ls tr[u].s[0]
#define rs tr[u].s[1]
#define lx tr[x].s[0]
#define rx tr[x].s[1]
}tr[N];
void pushup(int u){
tr[u].sum=tr[ls].sum+tr[u].v+tr[rs].sum;
if(u>n)tr[u].max=tr[u].min=tr[u].v;
else tr[u].max=-1e9,tr[u].min=1e9;
if(ls){
tr[u].max=max(tr[u].max,tr[ls].max);
tr[u].min=min(tr[u].min,tr[ls].min);
}
if(rs){
tr[u].max=max(tr[u].max,tr[rs].max);
tr[u].min=min(tr[u].min,tr[rs].min);
}
}
void pushrev(int u){
swap(ls,rs);
tr[u].rev^=1;
}
void pushlazy(int u){
tr[u].sum=-tr[u].sum;
tr[u].max=-tr[u].max;
tr[u].min=-tr[u].min;
tr[u].v=-tr[u].v;
swap(tr[u].max,tr[u].min);
tr[u].lazy^=1;
}
void pushdown(int u){
if(tr[u].rev){
pushrev(ls);
pushrev(rs);
tr[u].rev=0;
}
if(tr[u].lazy){
pushlazy(ls);
pushlazy(rs);
tr[u].lazy=0;
}
}
bool isroot(int u){
return tr[tr[u].p].s[0]!=u&&tr[tr[u].p].s[1]!=u;
}
void rotate(int x){
int y=tr[x].p,z=tr[y].p;
int k=tr[y].s[1]==x;
if(!isroot(y))tr[z].s[tr[z].s[1]==y]=x;
tr[x].p=z;
tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y,tr[y].p=x;
pushup(y),pushup(x);
}
void splay(int x){
int top=0,r=x;
stk[++top]=r;
while(!isroot(r))stk[++top]=r=tr[r].p;
while(top)pushdown(stk[top--]);
while(!isroot(x)){
int y=tr[x].p,z=tr[y].p;
if(!isroot(y)){
if((tr[z].s[1]==y)^(tr[y].s[1]==x))rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int z=x;
for(int y=0;x;y=x,x=tr[y].p){
splay(x);
tr[x].s[1]=y,pushup(x);
}
splay(z);
}
void makeroot(int x){
access(x);
pushrev(x);
}
int findroot(int x){
access(x);
while(lx)pushdown(x),x=lx;
splay(x);
return x;
}
void split(int x,int y){
makeroot(x);
access(y);
}
void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)tr[x].p=y;
}
void cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&rx==y&&!tr[y].s[0]){
tr[y].p=rx=0;
pushup(x);
}
}
void change(int x,int w){
splay(x);
tr[x].v=w;
pushup(x);
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
//for(int i=1;i<=n;i++)tr[i].max=-1e9,tr[i].min=1e9;
for(int i=1;i<n;i++){
int a,b,c;
cin>>a>>b>>c;
a++,b++;
tr[i+n].max=tr[i+n].min=tr[i+n].sum=tr[i+n].v=c;
link(a,i+n),link(i+n,b);
}
cin>>m;
while(m--){
string op;
int x,y;
cin>>op>>x>>y;
if(op=="C"){
change(x+n,y);continue;}
x++,y++;
if(op=="N"){
split(x,y);
tr[y].max=-tr[y].max;
tr[y].min=-tr[y].min;
swap(tr[y].max,tr[y].min);
tr[y].sum=-tr[y].sum;
tr[y].v=-tr[y].v;
tr[y].lazy^=1;
}
if(op=="SUM"){
split(x,y);
cout<<tr[y].sum<<'\n';
}
if(op=="MAX"){
split(x,y);
cout<<tr[y].max<<'\n';
}
if(op=="MIN"){
split(x,y);
cout<<tr[y].min<<'\n';
}
}
}
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
#define int long long
int n,m,p[N],stk[N],c[N];
struct node{
int s[2],p,v;
int c,lc,rc,sum,tag,rev;
#define ls tr[u].s[0]
#define rs tr[u].s[1]
#define lx tr[x].s[0]
#define rx tr[x].s[1]
}tr[N];
void pushup(int u){
if(ls)tr[u].lc=tr[ls].lc;
else tr[u].lc=tr[u].c;
if(rs)tr[u].rc=tr[rs].rc;
else tr[u].rc=tr[u].c;
if(ls&&rs)tr[u].sum=tr[ls].sum+tr[rs].sum+1-(tr[u].c==tr[ls].rc)-(tr[u].c==tr[rs].lc);
else if(ls)tr[u].sum=tr[ls].sum+1-(tr[u].c==tr[ls].rc);
else if(rs)tr[u].sum=tr[rs].sum+1-(tr[u].c==tr[rs].lc);
else if(!ls&&!rs)tr[u].sum=1;
}
void pushrev(int u){
swap(ls,rs);
swap(tr[u].lc,tr[u].rc);
tr[u].rev^=1;
}
void pushtag(int u,int y){
tr[u].lc=tr[u].rc=tr[u].c=y;
tr[u].tag=y;
tr[u].sum=1;
}
void pushdown(int u){
if(tr[u].rev){
if(ls)pushrev(ls);
if(rs)pushrev(rs);
tr[u].rev=0;
}
if(tr[u].tag){
if(ls)pushtag(ls,tr[u].tag);
if(rs)pushtag(rs,tr[u].tag);
tr[u].c=tr[u].lc=tr[u].rc=tr[u].tag;
tr[u].tag=0;
}
}
bool isroot(int u){
return tr[tr[u].p].s[0]!=u&&tr[tr[u].p].s[1]!=u;
}
void rotate(int x){
int y=tr[x].p,z=tr[y].p;
int k=tr[y].s[1]==x;
if(!isroot(y))tr[z].s[tr[z].s[1]==y]=x;
tr[x].p=z;
tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y,tr[y].p=x;
pushup(y),pushup(x);
}
void splay(int x){
int top=0,r=x;
stk[++top]=r;
while(!isroot(r))stk[++top]=r=tr[r].p;
while(top)pushdown(stk[top--]);
while(!isroot(x)){
int y=tr[x].p,z=tr[y].p;
if(!isroot(y)){
if((tr[z].s[1]==y)^(tr[y].s[1]==x))rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int z=x;
for(int y=0;x;y=x,x=tr[y].p){
splay(x);
tr[x].s[1]=y,pushup(x);
}
splay(z);
}
void makeroot(int x){
access(x);
pushrev(x);
}
int findroot(int x){
access(x);
while(lx)pushdown(x),x=lx;
splay(x);
return x;
}
void split(int x,int y){
makeroot(x);
access(y);
}
void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)tr[x].p=y;
}
void cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&rx==y&&!tr[y].s[0]){
tr[y].p=rx=0;
pushup(x);
}
}
void change(int x,int w){
splay(x);
tr[x].v=w;
pushup(x);
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>tr[i].c;
tr[i].lc=tr[i].rc=tr[i].c;
tr[i].sum=1;
}
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
link(a,b);
// tr[i+n].lc=c[a];
// tr[i+n].rc=c[b];
// if(c[a]!=c[b])tr[i+n].sum=1;
// link(a,i+n),link(i+n,b);
}
while(m--){
char op;
cin>>op;
int x,y,k;
if(op=='C'){
cin>>x>>y>>k;
split(x,y);
tr[y].tag=k;
}
else{
cin>>x>>y;
split(x,y);
cout<<tr[y].sum<<'\n';
}
}
}