[LOJ139]-樹鏈剖分

VictoryCzt發表於2018-12-26

題目地址

其實這個題不是個單純的模板,所以要做單純無腦的模板或者學習樹鏈剖分的可以去這裡【Luogu-樹鏈剖分】,這裡不會講樹鏈剖分的原理與實現。


開始想寫一些模板題目,luogu的寫得差不多了,所以就去loj看看,結果就看到了這道,emmmm

題目大意

給你一棵有點權的樹,開始時根為1號點,請你實現以下操作:

  • 換根
  • 一條鏈上點權加
  • 一個子樹內點權加
  • 詢問一條鏈上點權的和
  • 詢問一個子樹內的點權和

操作大多數和luogu的模板題一樣,且不用取模,但是多了一個換根操作。

所以對於鏈上的操作是不會影響的,只有子樹部分操作有影響。

首先考慮暴力,每次換根後重構,那麼複雜度顯然接受不了,所以我們要考慮不重構,所以開始的時候就以一號點為根,先把樹剖了,線段樹建出來。

此時,我們就要考慮不同的根和子樹操作該如何實現,我們假設根為rr,操作子樹的根節點為uu,那麼有如下幾種情況(原來的子樹是指的在根為1時的子樹):

  • u=ru=r,此時操作範圍就是整個樹,將整個樹加或者求和即可。

bef=========>=========>aft

  • rr不在uu的原來的子樹裡面時,操作範圍就是原來的子樹。

bef=========>=========>aft

  • rruu的子樹內時,情況就比較複雜,操作範圍就為整個樹減去uurr路徑上深度最小的點(不包含uu點)的原來的子樹。

bef=========>=========>aft

比如上面這個例子就是,我們現在6號點為根,查詢2號點,它的子樹就是1,3,5,也就是原來的整個樹減去4號點的原來的子樹剩餘部分,而4號點剛好是626\sim 2上不包含2的深度最小的點。


所以我們再子樹操作的時候再稍微判斷一下,分類討論求一下就好啦,求路上深度最小的點可以倍增往上跳,也可以直接樹剖的線段樹維護即可。

長長的程式碼:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int M=1e5+10;
const int inf=1e9;
int n,m,lg;
ll val[M];
struct ss{
	int to,last;
	ss(){}
	ss(int a,int b):to(a),last(b){}
}g[M<<1];
int head[M],cnt;
void add(int a,int b){
	g[++cnt]=ss(b,head[a]);head[a]=cnt;
	g[++cnt]=ss(a,head[b]);head[b]=cnt;
}
int dep[M],sze[M],top[M],son[M],f[M];
int num[M],rf[M],tim,root=1;
void dfs1(int a){
	sze[a]=1;
	for(int i=head[a];i;i=g[i].last){
		if(g[i].to==f[a]) continue;
		dep[g[i].to]=dep[a]+1;
		f[g[i].to]=a;
		dfs1(g[i].to);
		sze[a]+=sze[g[i].to];
		if(!son[a]||sze[son[a]]<sze[g[i].to])
		son[a]=g[i].to;
	}
}
void dfs2(int a,int b){
	top[a]=b;rf[num[a]=++tim]=a;
	if(!son[a]) return;
	dfs2(son[a],b);
	for(int i=head[a];i;i=g[i].last){
		if(g[i].to==f[a]||g[i].to==son[a]) continue;
		dfs2(g[i].to,g[i].to);
	}
} 

ll sum[M<<2],lazy[M<<2];
int minp[M<<2];
int tmin(int a,int b){
	if(a==inf)return b;
	if(b==inf)return a;
	if(dep[a]<dep[b])return a;
	else return b;
}
void pushup(int o){
	sum[o]=sum[o<<1]+sum[o<<1|1];
	minp[o]=tmin(minp[o<<1],minp[o<<1|1]);
}

void pushdown(int o,int l,int r,int mid){
	if(!lazy[o]) return;
	lazy[o<<1]+=lazy[o];
	lazy[o<<1|1]+=lazy[o];
	sum[o<<1]+=(mid-l+1)*lazy[o];
	sum[o<<1|1]+=(r-mid)*lazy[o];
	lazy[o]=0;
}

void build(int o,int l,int r){
	if(l==r){
		sum[o]=val[rf[l]];
		minp[o]=rf[l];
		return;
	}
	int mid=l+r>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	pushup(o);
}

void update(int o,int l,int r,int L,int R,ll v){
	if(L<=l&&r<=R){
		sum[o]+=(r-l+1)*v;
		lazy[o]+=v;
		return;
	}
	int mid=l+r>>1;
	pushdown(o,l,r,mid);
	if(L<=mid) update(o<<1,l,mid,L,R,v);
	if(R>mid) update(o<<1|1,mid+1,r,L,R,v);
	pushup(o);
}

ll query(int o,int l,int r,int L,int R){
	if(L<=l&&r<=R) return sum[o];
	int mid=l+r>>1;
	pushdown(o,l,r,mid);
	if(R<=mid) return query(o<<1,l,mid,L,R);
	else if(L>mid) return query(o<<1|1,mid+1,r,L,R);
	else return query(o<<1,l,mid,L,R)+query(o<<1|1,mid+1,r,L,R);
}

int find(int o,int l,int r,int L,int R){
	if(R<L)return inf;
	if(L<=l&&r<=R) return minp[o];
	int mid=l+r>>1;
	if(R<=mid) return find(o<<1,l,mid,L,R);
	else if(L>mid) return find(o<<1|1,mid+1,r,L,R);
	else return tmin(find(o<<1,l,mid,L,R),find(o<<1|1,mid+1,r,L,R));
}

int lca(int a,int b){
	while(top[a]!=top[b]){
		if(dep[top[a]]<dep[top[b]])swap(a,b);
		a=f[top[a]];
	}
	if(dep[a]>dep[b])swap(a,b);
	return a;
}

int Upto(int a,int b){
	int ans=inf,tw=a;
	while(top[a]!=top[b]){
		if(dep[top[a]]<dep[top[b]])swap(a,b);
		if(top[a]!=b)ans=tmin(ans,find(1,1,n,num[top[a]],num[a]));
		else ans=tmin(ans,find(1,1,n,num[top[a]]+1,num[a]));
		a=f[top[a]];
	}
	if(dep[a]>dep[b])swap(a,b);
	ans=tmin(ans,find(1,1,n,num[a]+1,num[b]));
	return ans;
}

int check(int a){
	if(a==root) return 0;
	int L=lca(a,root);
	if(L==a) return Upto(a,root);
	else return -1;
}

void Add_Chain(int a,int b,ll w){
	while(top[a]!=top[b]){
		if(dep[top[a]]<dep[top[b]])swap(a,b);
		update(1,1,n,num[top[a]],num[a],w);
		a=f[top[a]];
	}
	if(dep[a]>dep[b])swap(a,b);
	update(1,1,n,num[a],num[b],w);
}

ll Ask_Chain(int a,int b){
	ll ans=0;
	while(top[a]!=top[b]){
		if(dep[top[a]]<dep[top[b]])swap(a,b);
		ans+=query(1,1,n,num[top[a]],num[a]);
		a=f[top[a]];
	}
	if(dep[a]>dep[b])swap(a,b);
	ans+=query(1,1,n,num[a],num[b]);
	return ans;
}

void Add_Tree(int u,ll v){
	int type=check(u);
	if(!type){
		update(1,1,n,1,n,v);
	}else if(type>0){
		update(1,1,n,1,n,v);
		update(1,1,n,num[type],num[type]+sze[type]-1,-v);
	}else{
		update(1,1,n,num[u],num[u]+sze[u]-1,v);
	}
}

ll Ask_Tree(int u){
	int type=check(u);
	if(!type){
		return query(1,1,n,1,n);
	}else if(type>0){
		ll ans=query(1,1,n,num[type],num[type]+sze[type]-1);
		return query(1,1,n,1,n)-ans;
	}else{
		return query(1,1,n,num[u],num[u]+sze[u]-1);
	}
}
int opt,a,b,c;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&val[i]);
	for(int i=2;i<=n;i++){
		scanf("%d",&a);
		add(i,a);
	}
	dfs1(1);
	dfs2(1,1);
	build(1,1,n);
	for(scanf("%d",&m);m--;){
		scanf("%d",&opt);
		if(opt==1){
			scanf("%d",&a);
			root=a;
		}else if(opt==2){
			scanf("%d%d%d",&a,&b,&c);
			Add_Chain(a,b,c);
		}else if(opt==3){
			scanf("%d%d",&a,&b);
			Add_Tree(a,b);
		}else if(opt==4){
			scanf("%d%d",&a,&b);
			printf("%lld\n",Ask_Chain(a,b));
		}else{
			scanf("%d",&a);
			printf("%lld\n",Ask_Tree(a));
		}
	}
	return 0;
}

相關文章