Link Cut Tree
LCT专题总结
Part 1 LCT 原理介绍
(以下均为个人见解)
LCT是一种树链剖分,奇怪的树链剖分
根据我的认知,LCT是基于将整颗树随机地剖成若干条能够动态改变的链,每条链均是一条到达祖先的路径
整棵LCT就是由这样的链构成,并且这些链可以动态重构和连接
LCT最核心的操作就是\(Access\)
如何理解这个操作呢?
\(Access(x)\)就是构造出一条\(x\)到根的路径构成的链,并且构造完成后链上没有\(x\)的儿子
算法实现起来就是:
切掉\(x\)的儿子
\(while\) \(x\)所在链的顶端不是\(root\)
切掉\(fa[top[x]\)]的儿子
将\(x\)所在的链接在\(fa[top[x]]\)下面
我们亲爱的 \(tarjan\)老先生 证明了这个复杂度是\(log(n)\)的
那如何搞定这个断链接链的操作呢?
让\(Splay\)来搞定就行了
这样,我们的\(LCT\)算法,就是将原来的树,剖成了若干条链,每条链由一棵\(Splay\)维护,按照深度构树
Part 2 LCT基本操作
说的轻巧,写起来还是贼**
首先我么考虑存储
通常我们使用的\(LCT\)写法都是讲\(Splay\)里的\(fa\)数组和链与链之间的\(fa\)数组放在了一起
也就是说:
如果这个节点是它所在\(Splay\)的根,那么它的\(fa\)就是树上的\(fa\)
否则就是\(Splay\)上的\(fa\)
为了辨别它是不是Splay的跟,我们又引入了一个函数
inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }
\(Access\)
现在我们来看\(Access\)操作的代码实现
//while version
void Access(int x){
int t=0;
while(x){
Splay(x);
son[x][1]=t;
// 这里的这个操作不仅是将t接到了x上,同时也能把x的儿子断掉
x=fa[x];
}
}
//for version
void Access(int x){ for(int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t; }
tips:如果这道题有\(Up()\)操作,\(Access\)时别忘了
好我们先练几道模板题
[BZOJ 2002] Bounce 弹飞绵羊
就是维护深度,支持动态连边
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=2e5+10;
int n,m;
int fa[N],son[N][2],top[N],sz[N];
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }
void Up(int u) {
if(!u) return;
sz[u]=sz[son[u][0]]+sz[son[u][1]]+1;
}
void rotate(int u) {
int f=fa[u],ff=fa[f],d=dir(u);
fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
fa[f]=u,son[u][!d]=f;
Up(f),Up(u),Up(ff);
}
void Splay(int u) {
if(!u) return;
Up(u);
while(!isroot(u)) {
int f=fa[u];
if(!isroot(f)) {
if(dir(u)^dir(f)) rotate(u);
else rotate(f);
}
rotate(u);
}
}
void Access(int x) {
int t=0;
while(x) {
Splay(x);
son[x][1]=t;
Up(x);
x=fa[x];
}
}
int Que(int x) {
Access(x);
Splay(x);
return sz[son[x][0]];
}
void Link(int x,int to) {
Splay(x);
fa[son[x][0]]=fa[x],son[x][0]=0;
fa[x]=to;
}
int main(){
rep(i,1,n=rd()) {
int x=min(n+1,rd()+i);
fa[i]=x;
Up(i);
}
Up(n+1);
rep(i,1,rd()) {
int opt=rd();
if(opt==1) printf("%d\n",Que(rd()+1));
else {
int x=rd()+1,t=rd();
Link(x,min(n+1,x+t));
}
}
}
\[ \ \]
\[ \ \]
\(MakeRoot\)操作
即改变\(LCT\)的根
(由于博主能力有限,请读者画图自行理解一下)
void MakeRoot(int x){
Access(x);
Splay(x);
rev[x]^=1;
swap(son[x][0],son[x][1]);
}
就是构造一条到跟的链,然后将这条链翻转,翻转之后就能够让\(x\)变成根,同时树的其他部分并不需要改变
这个操作就要打标记了
[BZOJ 2049] Cave 洞穴勘测
这道题要多一个\(GetRoot\)操作,这个也不必多讲了吧
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=1e4+10;
int n,m;
int fa[N],son[N][2];
int rev[N];
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x] || (son[fa[x]][0]!=x && son[fa[x]][1]!=x); }
void Down(int u) {
if(!u || !rev[u]) return;
int t;
if(son[u][0]) {
t=son[u][0];
rev[t]^=1;
swap(son[t][0],son[t][1]);
}
if(son[u][1]) {
t=son[u][1];
rev[t]^=1;
swap(son[t][0],son[t][1]);
}
rev[u]=0;
}
void rotate(int u) {
int f=fa[u],ff=fa[f];
if(!isroot(f)) Down(ff);
Down(f),Down(u);
int d=dir(u);
fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
fa[f]=u,son[u][!d]=f;
}
void Splay(int u) {
Down(u);
while(!isroot(u)) {
int f=fa[u];
if(!isroot(f)) {
if(dir(u)^dir(f)) rotate(u);
else rotate(f);
}
rotate(u);
}
}
void Access(int x) {
int t=0;
while(x) {
Splay(x);
son[x][1]=t,fa[t]=x,t=x;
x=fa[x];
}
}
void makeroot(int x) {
Access(x);
Splay(x);
rev[x]^=1;
swap(son[x][0],son[x][1]);
}
int GetRoot(int x){
Access(x),Splay(x);
Down(x);
while(son[x][0]) {
x=son[x][0];
Down(x);
}
Splay(x);
return x;
}
int Que(int x,int y) {
return GetRoot(x)==GetRoot(y);
}
void Link(int x,int y) {
makeroot(x);
fa[x]=y;
}
void Cut(int x,int y) {
makeroot(x);
Access(y);
Splay(y);
fa[x]=0;
son[y][0]=0;
}
char opt[10];
int main(){
n=rd(),m=rd();
rep(i,1,m) {
scanf("%s",opt);
if(opt[0]=='Q') puts(Que(rd(),rd())?"Yes":"No");
else if(opt[0]=='C') Link(rd(),rd());
else Cut(rd(),rd());
}
}
\[ \ \]
\[ \ \]
路径查询
据我所知,路径查询有两种写法
一种是先\(Access(x)\),再把\(y\) \(Access\)到\(x\)下面(部分\(Access\)),然后就有几种情况讨论一下
当然可以直接就\(MakeRoot(x),Access(y),Splay(y)\),直接访问,简单粗暴,但是要打标记
[SPOJ QTREE] Query on a tree
树剖线段树裸题。。。
但是\(LCT\)单\(log\)!
由于边是静态的,所以就是一个简单的路径查询
这里我唯一一次写了第一种查询
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=1e4+10;
int n,m;
int fa[N],son[N][2],s[N],val[N];
int id[N],num[N];
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }
void Up(int u) {
s[u]=max(val[u],max(s[son[u][0]],s[son[u][1]]));
}
void rotate(int u){
int f=fa[u],ff=fa[f],d=dir(u);
int t=isroot(f);
fa[u]=ff; if(!t) son[ff][dir(f)]=u;
son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
fa[f]=u,son[u][!d]=f;
Up(f),Up(u);
if(!t) Up(ff);
}
void Splay(int u) {
while(!isroot(u)) {
int f=fa[u];
if(!isroot(f)) {
if(dir(u)^dir(f)) rotate(u);
else rotate(f);
}
rotate(u);
}
}
int Que(int x,int y) {
if(x==y) return 0;
int t1=0,t2=0,tx=x;
while(x) {
Splay(x);
son[x][1]=t1;
Up(x); t1=x; x=fa[x];
}
while(y) {
Splay(y);
if(!fa[y]) break;
son[y][1]=t2;
Up(y); t2=y; y=fa[y];
}
if(!t2) return s[son[y][1]];
int ans=s[t2];
if(t2==tx) return ans;
ans=max(ans,s[son[y][1]]);
return ans;
}
struct Edge{
int to,nxt,w,id;
}e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v,int w,int id) {
e[++ecnt]=(Edge){v,head[u],w,id};
head[u]=ecnt;
}
void pre_dfs(int u,int f) {
son[u][0]=son[u][1]=0;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].to;
if(v==f) continue;
id[e[i].id]=v,fa[v]=u;
s[v]=val[v]=e[i].w;
pre_dfs(v,u);
}
}
char opt[10];
int main(){
rep(kase,1,rd()) {
n=rd();
rep(i,1,n) head[i]=0; ecnt=0;
rep(i,1,n-1) {
int u=rd(),v=rd(),w=rd();
AddEdge(u,v,w,i);
AddEdge(v,u,w,i);
}
fa[1]=0;
pre_dfs(1,0);
while(~scanf("%s",opt) && opt[0]!='D') {
if(opt[0]=='Q') printf("%d\n",Que(rd(),rd()));
else {
int x=rd(),y=rd();
val[id[x]]=y;
Splay(id[x]);
}
}
}
}
\[ \ \]
\[ \ \]
HDU - 4010
多了几个操作,\(Debug\)警告!
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=3e5+10;
int n,m;
int fa[N],son[N][2];
int s[N],val[N],t[N];
int rev[N];
struct Edge{
int to,nxt;
}e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v){
e[++ecnt]=(Edge){v,head[u]};
head[u]=ecnt;
}
inline int dir(int x) { return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x] || (son[fa[x]][0]!=x && son[fa[x]][1]!=x); }
void Down(int u) {
if(!u) return;
if(rev[u]) {
if(son[u][0]) {
rev[son[u][0]]^=1;
swap(son[son[u][0]][0],son[son[u][0]][1]);
}
if(son[u][1]) {
rev[son[u][1]]^=1;
swap(son[son[u][1]][0],son[son[u][1]][1]);
}
rev[u]=0;
}
if(t[u]) {
if(son[u][0]) {
t[son[u][0]]+=t[u];
s[son[u][0]]+=t[u];
val[son[u][0]]+=t[u];
}
if(son[u][1]) {
t[son[u][1]]+=t[u];
s[son[u][1]]+=t[u];
val[son[u][1]]+=t[u];
}
t[u]=0;
}
}
void Up(int u) {
if(!u) return;
s[u]=max(val[u],max(s[son[u][0]],s[son[u][1]]));
}
void rotate(int u) {
int t=!isroot(fa[u]);
if(t) Down(fa[fa[u]]);
Down(fa[u]),Down(u);
int f=fa[u],ff=fa[f],d=dir(u);
fa[u]=ff; if(t) son[ff][dir(f)]=u;
son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
fa[f]=u,son[u][!d]=f;
Up(f),Up(u);
if(t) Up(ff);
}
void Splay(int u) {
static int stk[N],top=0;
int t=u;
while(1) {
stk[++top]=t;
if(isroot(t)) break;
t=fa[t];
}
while(top) Down(stk[top--]);
Down(u);
while(!isroot(u)) {
int f=fa[u];
if(!isroot(f)) {
if(dir(u)^dir(f)) rotate(u);
else rotate(f);
}
rotate(u);
}
}
void Access(int x) {
int t=0;
while(x) {
Splay(x);
son[x][1]=t;
Up(x);
t=x,x=fa[x];
}
}
void pre_dfs(int u,int f) {
fa[u]=f;
son[u][0]=son[u][1]=rev[u]=t[u]=0;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].to;
if(v==f) continue;
pre_dfs(v,u);
}
}
int GetRoot(int x) {
Access(x);
Splay(x);
while(1) {
Down(x);
if(!son[x][0]) break;
x=son[x][0];
}
Splay(x);
return x;
}
void MakeRoot(int x) {
Access(x);
Splay(x);
rev[x]^=1;
swap(son[x][0],son[x][1]);
}
void Link(int x,int y) {
if(GetRoot(x)==GetRoot(y)) return (void)puts("-1");
MakeRoot(x);
fa[x]=y;
}
void Cut(int x,int y) {
swap(x,y);
if(x==y || GetRoot(x)!=GetRoot(y)) return (void)puts("-1");
MakeRoot(x);
Access(y);
Splay(y);
fa[son[y][0]]=0;
son[y][0]=0;
}
void Add(int x,int y,int w) {
if(GetRoot(x)!=GetRoot(y)) return (void)puts("-1");
MakeRoot(x);
Access(y);
Splay(y);
s[y]+=w,t[y]+=w,val[y]+=w;
}
int Que(int x,int y){
if(GetRoot(x)!=GetRoot(y)) return -1;
MakeRoot(x);
Access(y);
Splay(y);
return s[y];
}
int main(){
while(~scanf("%d",&n)) {
rep(i,1,n) head[i]=0; ecnt=0;
rep(i,2,n) {
int u=rd(),v=rd();
AddEdge(u,v);
AddEdge(v,u);
}
rep(i,1,n) s[i]=val[i]=rd();
pre_dfs(1,0);
rep(ttt,1,m=rd()) {
int opt=rd();
if(opt==1) Link(rd(),rd());
if(opt==2) Cut(rd(),rd());
if(opt==3) {
int w=rd(),x=rd(),y=rd();
Add(x,y,w);
}
if(opt==4) printf("%d\n",Que(rd(),rd()));
}
puts("");
}
}
\[ \ \]
\[ \ \]
HDU - 5052
城市贸易动态版
如果要\(MakeRoot\)打翻转标记的话,存一个正的存一个反的直接换就行了
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=5e4+10;
int n,m;
int fa[N],son[N][2];
inline int max(int x,int y) { return x>y?x:y; }
inline int min(int x,int y) { return x
HDU - 5398
这题需要我们弄一些小性质
假设我们从大的数向小的数连边,那么一定是向大的数的因子连边
如果这条边已经存在,那么就要从当前产生的环上换下来,存一个最小值
这样就是\(LCT\)模拟连边过程,总复杂度$n \cdot log n \cdot ln n $
#include
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=2e5+10;
int n=1e5;
vector fac[N];
int fa[N],son[N][2];
int rev[N];
struct Node{
int x,id;
Node operator + (const Node t) const{
Node res;
res.x=min(x,t.x);
if(res.x==x) res.id=id;
else res.id=t.id;
return res;
}
} s[N],val[N];
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x] || (son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }
void Up(int u) {
if(!u) return;
s[u]=val[u];
if(son[u][0]) s[u]=s[u]+s[son[u][0]];
if(son[u][1]) s[u]=s[u]+s[son[u][1]];
}
void Down(int u) {
if(!u||!rev[u]) return;
int t;
if(son[u][0]) {
t=son[u][0];
rev[t]^=1;
swap(son[t][0],son[t][1]);
}
if(son[u][1]) {
t=son[u][1];
rev[t]^=1;
swap(son[t][0],son[t][1]);
}
rev[u]=0;
}
int stk[N],top;
int eu[N],ev[N];
void rotate(int u) {
int f=fa[u],ff=fa[f];
Down(ff),Down(f),Down(u);
int d=dir(u);
fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
son[f][d]=son[u][!d]; fa[son[u][!d]]=f;
fa[f]=u,son[u][!d]=f;
Up(f),Up(u),Up(ff);
}
void Splay(int x) {
Down(x),Up(x);
static int stk[N],top=0;
int t=x;
while(!isroot(t)) stk[++top]=t,t=fa[t];
stk[++top]=t;
while(top) Down(stk[top--]);
while(!isroot(x)) {
int f=fa[x];
if(!isroot(f)) {
if(dir(x)^dir(f)) rotate(x);
else rotate(f);
}
rotate(x);
}
}
void Access(int x){ for(int t=0; x; t=x,x=fa[x]) Splay(x),son[x][1]=t,Up(x); }
void MakeRoot(int x) {
Access(x);
Splay(x);
rev[x]^=1;
swap(son[x][0],son[x][1]);
}
int GetRoot(int x) {
Access(x);
Splay(x);
do {
Down(x);
if(!son[x][0]) break;
x=son[x][0];
} while(1);
Splay(x);
return x;
}
void Cut(int x,int y) {
MakeRoot(x);
Access(y);
Splay(x);
fa[y]=0;
son[x][1]=0;
Up(x);
}
ll ans;
ll res[N];
void Link(int x,int y,int w) {
if(GetRoot(x)==GetRoot(y)) {
MakeRoot(x);
Access(y);
Node t=s[y];
if(t.x>=w) return;
ans-=t.x;
Cut(eu[t.id],t.id);
Cut(ev[t.id],t.id);
son[t.id][0]=son[t.id][1]=fa[t.id]=rev[t.id]=0;
stk[++top]=t.id;
}
MakeRoot(y);
int t=stk[top--];
fa[t]=rev[t]=0,son[t][0]=son[t][1]=0,s[t]=val[t]=(Node){w,t};
eu[t]=x,ev[t]=y;
ans+=w;
fa[t]=x;
fa[y]=t;
}
int main(){
rep(i,n+1,2*n) stk[++top]=i;
rep(i,1,n) s[i]=val[i]=(Node){(int)1e9,i};
for(reg int i=1;i<=n;++i) for(reg int j=i+i;j<=n;j+=i) fac[j].push_back((int)i);
rep(i,2,n) {
drep(j,fac[i].size()-1,0) Link(i,fac[i][j],fac[i][j]);
res[i]=ans;
}
while(~scanf("%d",&n)) printf("%lld\n",res[n]);
}
HDU - 5333
询问离线,从左到右,每次将\(max(u,v) \leq i\)的边加入,同时按照\(min(u,v)\)较大的顺序将边保留,保证当前图是一棵生成树,并且图上的边是每一个环上最大的(即动态维护最大生成树),用树状数组维护边权\(\geq j\)的总数,即联通块减少的数量
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=2e5+10;
int n,m,q;
struct Edge{
int u,v;
int operator < (const Edge __) const {
return max(u,v)=w) return;
Cut(eu[t.id],t.id);
Cut(ev[t.id],t.id);
B.Add(t.x,-1);
stk[++top]=t.id;
}
MakeRoot(y);
int t=stk[top--];
eu[t]=x,ev[t]=y;
son[t][0]=son[t][1]=rev[t]=fa[t]=0,s[t]=val[t]=(Node){w,t};
fa[t]=x;
fa[y]=t;
B.Add(w,1);
}
int main(){
while(~scanf("%d%d%d",&n,&m,&q)) {
top=0;
rep(i,n+1,n*2) stk[++top]=i;
rep(i,1,n*2) fa[i]=son[i][0]=son[i][1]=rev[i]=0;
rep(i,1,n) s[i]=val[i]=(Node){(int)1e9,0};
rep(i,1,m) E[i].u=rd(),E[i].v=rd();
sort(E+1,E+m+1);
rep(i,1,q) Q[i].l=rd(),Q[i].r=rd(),Q[i].id=i;
sort(Q+1,Q+q+1);
int p1=1,p2=1;
B.init();
rep(i,1,n) {
while(p1<=m && E[p1].u<=i&&E[p1].v<=i) {
Link(E[p1].u,E[p1].v,min(E[p1].u,E[p1].v));
p1++;
}
while(p2<=q && Q[p2].r<=i) {
Ans[Q[p2].id]=B.Que(Q[p2].r)-B.Que(Q[p2].l-1);
p2++;
}
}
rep(i,1,q) printf("%d\n",n-Ans[i]);
}
}
\[ \ \]
\[ \ \]
HDU - 5967
动态维护基环内向树
将树边直接加入,非树边更新根的\(mark\)值
查询时\(GetRoot\)然后根据\(mark\)值判断
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=2e5+10;
int n,m;
int fa[N],son[N][2];
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }
void rotate(int u){
int f=fa[u],ff=fa[f],d=dir(u);
fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
fa[f]=u; son[u][!d]=f;
}
void Splay(int x) {
while(!isroot(x)) {
int f=fa[x];
if(!isroot(f)) {
if(dir(x)^dir(f)) rotate(x);
else rotate(f);
}
rotate(x);
}
}
void Access(int x){ for(int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t; }
int GetRoot(int x) {
Access(x),Splay(x);
while(son[x][0]) x=son[x][0];
Splay(x);
return x;
}
int mk[N];
void Link(int x,int to) {
if(!to) return;
if(GetRoot(x)==GetRoot(to)) {
mk[x]=to;
return;
} else mk[x]=0;
Splay(x);
fa[x]=to;
}
void Cut(int x) {
int rt=GetRoot(x);
if(x==rt) {
mk[x]=0;
return;
}
Access(x);
Splay(x);
fa[son[x][0]]=0;
son[x][0]=0;
if(mk[rt]) Link(rt,mk[rt]);
}
int main(){
n=rd(),m=rd();
rep(i,1,n) Link(i,rd());
rep(i,1,m) {
int opt=rd();
if(opt==1) {
int x=rd();
Cut(x);
Link(x,rd());
} else {
int rt=GetRoot(rd());
if(mk[rt]) puts("-1");
else printf("%d\n",rt);
}
}
}
\[ \ \]
\[ \ \]
[BZOJ - 2759] 一个动态树好题
这个题其实也和上一题差不多
但是要维护路径迭代和,同时mark数组也要存非树边
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=2e5+10,P=1e4+7;
int n,m;
int fa[N],son[N][2];
struct Node{
int x,y;
Node operator + (const Node t) const {
return (Node){x*t.x%P,(t.x*y+t.y)%P};
}
} s[N],val[N],t[N];
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }
int Inv[P];
int mk[N];
void Up(int u) {
if(!u) return;
s[u]=val[u];
if(son[u][0]) s[u]=s[son[u][0]]+s[u];
if(son[u][1]) s[u]=s[u]+s[son[u][1]];
}
void rotate(int u){
int f=fa[u],ff=fa[f],d=dir(u);
fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
fa[f]=u; son[u][!d]=f;
Up(f),Up(u),Up(ff);
}
void Splay(int u) {
while(!isroot(u)) {
int f=fa[u];
if(!isroot(f)) {
if(dir(u)^dir(f)) rotate(u);
else rotate(f);
}
rotate(u);
}
}
void Access(int x){ for(reg int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t,Up(x); }
int GetRoot(int x) {
Access(x);
Splay(x);
while(son[x][0]) x=son[x][0];
Splay(x);
return x;
}
void Link(int x,int to) {
s[x]=val[x];
if(GetRoot(x)==GetRoot(to)) {
t[x]=val[x];
s[x]=val[x]=(Node){1,0};
mk[x]=to;
return;
}
Access(x);
Splay(x);
fa[x]=to;
}
void Cut(int x) {
if(mk[x]) {
mk[x]=0;
s[x]=val[x]=t[x];
return;
}
int rt=GetRoot(x);
Access(x);
Splay(x);
fa[son[x][0]]=0;
son[x][0]=0;
if(mk[rt] && GetRoot(rt)!=GetRoot(mk[rt])) {
s[rt]=val[rt]=t[rt];
Link(rt,mk[rt]);
mk[rt]=0;
}
}
int Solve(int x) {
int y=mk[x];
if(!y) return -2;
Access(y);
Splay(y);
Node tmp=s[y];
tmp=tmp+t[x];
tmp.x=(1-tmp.x+P)%P;
if(tmp.x==0) {
if(tmp.y==0) return -2;
if(tmp.y!=0) return -1;
}
tmp.y=tmp.y*Inv[tmp.x]%P;
return tmp.y;
}
int Que(int x) {
int rt=GetRoot(x);
rt=Solve(rt);
Access(x);
Splay(x);
Node t=s[x];
if(t.x==0) return t.y;
if(rt<0) return rt;
return (t.x*rt+t.y)%P;
}
char opt[10];
int main(){
Inv[0]=Inv[1]=1;
rep(i,2,P-1) Inv[i]=(P-P/i)*Inv[P%i]%P;
n=rd();
rep(i,1,n) val[i].x=1;
rep(i,1,n) {
val[i].x=rd();
int v=rd();
val[i].y=rd();
Link(i,v);
}
m=rd();
rep(i,1,m) {
scanf("%s",opt);
if(opt[0]=='A') printf("%d\n",Que(rd()));
else {
int x=rd();
Cut(x);
val[x].x=rd();
int v=rd();
val[x].y=rd();
Link(x,v);
}
}
}
HDU - 5467
建立状态树,在状态树上回溯更新
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=3e5+10,P=1e9+7;
int n,m;
int fa[N],son[N][2],rev[N];
ll s[N],val[N];
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }
void Up(int u) {
if(!u) return;
s[u]=val[u];
if(son[u][0]) s[u]=s[u]*s[son[u][0]]%P;
if(son[u][1]) s[u]=s[u]*s[son[u][1]]%P;
}
void Down(int u) {
if(!u||!rev[u]) return;
rep(i,0,1) {
int t=son[u][i];
if(!t) continue;
rev[t]^=1;
swap(son[t][0],son[t][1]);
}
rev[u]=0;
}
void rotate(int u) {
int f=fa[u],ff=fa[f],d=dir(u);
fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
fa[f]=u,son[u][!d]=f;
Up(f),Up(u),Up(ff);
}
void Splay(int u) {
static int stk[N],top=0;
int t=u;
Up(u);
while(!isroot(t)) stk[++top]=t,t=fa[t];
stk[++top]=t;
while(top) Down(stk[top--]);
while(!isroot(u)) {
int f=fa[u];
if(!isroot(f)) {
if(dir(u)^dir(f)) rotate(u);
else rotate(f);
}
rotate(u);
}
}
void Access(int x){ for(int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t,Up(x); }
int GetRoot(int x){
Access(x),Splay(x);
while(son[x][0]) Down(x),x=son[x][0];
Splay(x);
return x;
}
void MakeRoot(int x) {
Access(x),Splay(x);
rev[x]^=1;
swap(son[x][0],son[x][1]);
}
int suc[N];
int Cut(int x,int y) {
if(GetRoot(x)!=GetRoot(y)) return 0;
MakeRoot(x),Access(y);
Splay(x);
if(son[x][1]!=y || fa[y]!=x || son[y][0]) return 0;
son[x][1]=fa[y]=0,Up(x);
return 1;
}
int Link(int x,int y) {
if(GetRoot(x)==GetRoot(y)) return 0;
MakeRoot(y);Splay(y);
fa[y]=x;
return 1;
}
ll Que(int x,int y) {
if(GetRoot(x)!=GetRoot(y)) return 0;
MakeRoot(x);
Access(y);
Splay(y);
return s[y];
}
vector G[N];
int opt[N],a[N],b[N];
ll Ans[N];
void dfs(int u) {
if(opt[u]==1) suc[u]=Link(a[u],b[u]);
if(opt[u]==2) suc[u]=Cut(a[u],b[u]);
if(opt[u]==4) Ans[u]=Que(a[u],b[u]);
if(opt[u]==5) {
suc[u]=val[a[u]],val[a[u]]=b[u]*(b[u]-1)/2;
Splay(a[u]);
}
rep(i,0,G[u].size()-1) dfs(G[u][i]);
if(opt[u]==1 && suc[u]) Cut(a[u],b[u]);
if(opt[u]==2 && suc[u]) Link(a[u],b[u]);
if(opt[u]==5) {
val[a[u]]=suc[u];
Splay(a[u]);
}
}
int main(){
rep(kase,1,rd()) {
n=rd(),m=rd();
rep(i,1,n) {
val[i]=rd(),val[i]=val[i]*(val[i]-1)/2;
s[i]=val[i];
fa[i]=son[i][0]=son[i][1]=rev[i]=0;
}
rep(i,0,m) G[i].clear();
int now=0;
rep(i,1,m) {
opt[i]=rd();
if(opt[i]==3) now=rd();
else a[i]=rd(),b[i]=rd();
G[now].push_back((int)i);
now=i;
}
dfs(0);
rep(i,1,m) if(opt[i]==4) printf("%lld\n",Ans[i]);
}
}
\[ \ \]
\[ \ \]
子树查询
另开一个数组,存下树上其他儿子的总和,这个维护起来比较得**
因为在很多时候都要更新,所以细节贼多,不好口述
HDU - 5405
设所有点的总和\(Sum\)
将路径拉成一条链,就是求\(Sum^2-\)链上所有节点树儿子权值平方和
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=2e5+10,P=1e9+7;
int n,m;
ll a[N];
ll ts[N],s[N],qs[N],sqs[N];
int rev[N];
ll Sum;
struct Edge{
int to,nxt;
} e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v) {
e[++ecnt]=(Edge){v,head[u]};
head[u]=ecnt;
}
#define erep(u,i) for(int i=head[u];i;i=e[i].nxt)
void Mod(ll &x,ll y) {
x+=y;
x=(x%P+P)%P;
}
int fa[N],son[N][2];
void pre_dfs(int u,int f) {
fa[u]=f;
qs[u]=ts[u]=0;
son[u][0]=son[u][1]=0;
erep(u,i) {
int v=e[i].to;
if(v==f) continue;
pre_dfs(v,u);
}
s[u]=(a[u]+ts[u])%P;
Mod(ts[f],s[u]);
Mod(qs[f],s[u]*s[u]%P);
sqs[u]=qs[u];
}
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }
void Up(int u) {
if(!u) return;
s[u]=(ts[u]+a[u])%P;
sqs[u]=qs[u];
if(son[u][0]) Mod(s[u],s[son[u][0]]),Mod(sqs[u],sqs[son[u][0]]);
if(son[u][1]) Mod(s[u],s[son[u][1]]),Mod(sqs[u],sqs[son[u][1]]);
}
void Down(int u) {
if(!u || !rev[u]) return;
int t;
rep(i,0,1) {
t=son[u][i];
if(!t) continue;
rev[t]^=1;
swap(son[t][0],son[t][1]);
}
rev[u]=0;
}
void rotate(int u) {
int f=fa[u],ff=fa[f],d=dir(u);
fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
fa[f]=u,son[u][!d]=f;
Up(f),Up(u),Up(ff);
}
void Splay(int u) {
static int stk[N],top=0;
int t=u;
while(!isroot(t)) stk[++top]=t,t=fa[t];
stk[++top]=t;
while(top) Down(stk[top--]);
while(!isroot(u)) {
int f=fa[u];
if(!isroot(f)) {
if(dir(u)^dir(f)) rotate(u);
else rotate(f);
}
rotate(u);
}
}
void Access(int x) {
for(reg int t=x;t;t=fa[t]) Splay(t);
for(reg int t=0,y=x;y;t=y,y=fa[y]) {
if(t) {
Mod(ts[y],-s[t]);
Mod(qs[y],-s[t]*s[t]%P);
}
}
for(reg int t=0;x;t=x,x=fa[x]) {
Splay(x);
if(son[x][1]) Mod(ts[x],s[son[x][1]]),Mod(qs[x],s[son[x][1]]*s[son[x][1]]%P);
son[x][1]=t;
Up(x);
}
}
void MakeRoot(int x) {
Access(x);
Splay(x);
rev[x]^=1;
swap(son[x][0],son[x][1]);
}
void Upd(int x,int d) {
Access(x);
Splay(x);
Sum-=a[x];
Sum+=(a[x]=d);
Up(x);
}
void Que(int x,int y){
MakeRoot(x);
Access(y); Splay(y);
ll ans=(Sum%P*(Sum%P))%P-sqs[y];
Mod(ans,0);
printf("%lld\n",ans);
}
int main(){
while(~scanf("%d%d",&n,&m)) {
ecnt=Sum=0;
rep(i,1,n) a[i]=rd(),head[i]=0,Sum+=a[i];
rep(i,2,n) {
int u=rd(),v=rd();
AddEdge(u,v);
AddEdge(v,u);
}
pre_dfs(1,0);
rep(i,1,m) {
int opt=rd();
if(opt==1) {
int x=rd(),w=rd();
Upd(x,w);
} else Que(rd(),rd());
}
}
}