zoukankan      html  css  js  c++  java
  • Link Cut Tree

    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<cstdio>
    #include<cctype>
    #include<iostream>
    #include<cassert>
    #include<cmath>
    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<cstdio>
    #include<cctype>
    #include<iostream>
    #include<cassert>
    #include<cmath>
    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<cstdio>
    #include<cctype>
    #include<iostream>
    #include<cassert>
    #include<cmath>
    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<cstdio>
    #include<cctype>
    #include<iostream>
    #include<cassert>
    #include<cmath>
    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<cstdio>
    #include<cctype>
    #include<iostream>
    #include<cassert>
    #include<cmath>
    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<y?x:y; }
    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);  }
    
    struct Node{
    	int ma,mi,ans,rans;
    	Node operator + (const Node x) const {
    		Node res;
    		res.ma=max(ma,x.ma);
    		res.mi=min(mi,x.mi);
    		res.ans=max(max(ans,x.ans),ma-x.mi);
    		res.rans=max(max(rans,x.rans),x.ma-mi);
    		return res;
    	}
    }s[N],val[N];
    
    
    int rev[N],t[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 Down(int u) {
    	if(!u) return;
    	int x;
    	if(rev[u]) {
    		if(son[u][0]) {
    			x=son[u][0];
    			rev[x]^=1;
    			swap(son[x][0],son[x][1]);
    			swap(s[x].ans,s[x].rans);
    		}
    		if(son[u][1]) {
    			x=son[u][1];
    			rev[x]^=1;
    			swap(son[x][0],son[x][1]);
    			swap(s[x].ans,s[x].rans);
    		}
    		rev[u]=0;
    	}
    	if(t[u]) {
    		if(son[u][0]) {
    			x=son[u][0];
    			t[x]+=t[u];
    			s[x].ma+=t[u],s[x].mi+=t[u],val[x].ma+=t[u],val[x].mi+=t[u];
    		}
    		if(son[u][1]) {
    			x=son[u][1];
    			t[x]+=t[u];
    			s[x].ma+=t[u],s[x].mi+=t[u],val[x].ma+=t[u],val[x].mi+=t[u];
    		}
    		t[u]=0;
    	}
    }
    
    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]; 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 x){
    	Down(x);
    	while(!isroot(x)) {
    		int f=fa[x];
    		if(!isroot(f)) {
    			if(dir(f)^dir(x)) 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]);
    	swap(s[x].rans,s[x].ans);
    }
    
    int Que(int x,int y,int w) {
    	MakeRoot(x);
    	Access(y);
    	Splay(y);
    	int t=s[y].ans;
    	::t[y]+=w,s[y].ma+=w,s[y].mi+=w;
    	val[y].mi+=w,val[y].ma+=w;
    	return t;
    }
    
    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 pre_dfs(int u,int f) {
    	fa[u]=f;son[u][0]=son[u][1]=t[u]=rev[u]=0;
    	erep(u,i) {
    		int v=e[i].to;
    		if(v==f) continue;
    		pre_dfs(v,u);
    	}
    }
    
    int main(){
    	rep(kase,1,rd()) {
    		n=rd();
    		rep(i,1,n) {
    			int x=rd();
    			head[i]=0;
    			s[i]=val[i]=(Node){x,x,0,0};
    		}
    		ecnt=0;
    		rep(i,2,n) {
    			int u=rd(),v=rd();
    			AddEdge(u,v);
    			AddEdge(v,u);
    		}
    		pre_dfs(1,0);
    		rep(i,1,m=rd()) {
    			int y=rd(),x=rd(),w=rd();
    			printf("%d\n",Que(x,y,w));
    		}
    	}
    }
    
    

    HDU - 5398

    这题需要我们弄一些小性质

    假设我们从大的数向小的数连边,那么一定是向大的数的因子连边

    如果这条边已经存在,那么就要从当前产生的环上换下来,存一个最小值

    这样就是\(LCT\)模拟连边过程,总复杂度$n \cdot log \ n \cdot ln \ n $

    #include<cstdio>
    #include<cctype>
    #include<iostream>
    #include<vector>
    #include<cmath>
    #include<algorithm>
    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 <int> 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<cstdio>
    #include<cctype>
    #include<iostream>
    #include<vector>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    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)<max(__.u,__.v);
    	}
    }E[N];
    struct Node{
    	int x,id;
    	Node operator + (const Node __) const{
    		Node res;
    		res.x=min(x,__.x);
    		if(res.x==x) res.id=id;
    		else res.id=__.id;
    		return res;
    	}
    }s[N],val[N];
    struct Query{
    	int l,r,id;
    	bool operator < (const Query __) const{
    		return r<__.r;
    	}
    }Q[N];
    int Ans[N];
    int eu[N],ev[N];
    int stk[N],top;
    int fa[N],son[N][2],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 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;
    }
    
    
    void rotate(int u) {
    	int f=fa[u],ff=fa[f];
    	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;
    	Up(f),Up(u),Up(ff);
    }
    void Splay(int 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);
    	while(son[x][0]) Down(x),x=son[x][0];
    	Splay(x);
    	return x;
    }
    
    
    void Cut(int x,int y) {
    	MakeRoot(x),Access(y);
    	Splay(x);
    	if(son[x][1]!=y || fa[y]!=x) puts("WTF Cut?");
    	son[x][1]=fa[y]=0;
    	Up(x);
    }
    
    struct BIT{ 
    	int s[N];
    	void init(){ memset(s,0,sizeof s); }
    	void Add(int p,int x) {
    		while(p<=n) s[p]+=x,p+=p&-p;
    	}
    	int Que(int p) {
    		int res=0;
    		while(p) res+=s[p],p-=p&-p;
    		return res;
    	}
    } B;
    
    void Link(int x,int y,int w) {
    	if(GetRoot(x)==GetRoot(y)) {
    		MakeRoot(x),Access(y);
    		Splay(y);
    		Node t=s[y];
    		if(t.x>=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<cstdio>
    #include<cctype>
    #include<iostream>
    #include<vector>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    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<cstdio>
    #include<cctype>
    #include<iostream>
    #include<vector>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    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<cstdio>
    #include<cctype>
    #include<iostream>
    #include<vector>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    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 <int> 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<cstdio>
    #include<cctype>
    #include<iostream>
    #include<vector>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    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());
            }
        }
    }
    
    
  • 相关阅读:
    ASP.NET身份验证机制membership入门——API篇
    测试SQL语句的执行时间
    ASP.NET身份验证机制membership入门——配置篇(2)
    ASP.NET身份验证机制membership入门——配置篇(1)
    ASP.NET身份验证机制membership入门——项目
    ASP.NET用户个性化设置Profile——配置1
    POJ 2762 强连通分量中存在单相连通边 【tarjan+toposort+缩点】.cpp
    POJ 2516 【最小费用最大流】.cpp
    POJ 1904 【强连通分量】.cpp
    POJ 1236 【强连通图+缩点】.cpp
  • 原文地址:https://www.cnblogs.com/chasedeath/p/11674507.html
Copyright © 2011-2022 走看看