树链剖分套树状数组(区间修改)板子

Posted Stump

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树链剖分套树状数组(区间修改)板子相关的知识,希望对你有一定的参考价值。

P3384 【模板】树链剖分

题目描述

如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:

操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z

操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和

操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z

操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和

输入输出格式

输入格式:

 

第一行包含4个正整数N、M、R、P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。

接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。

接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通)

接下来M行每行包含若干个正整数,每行表示一个操作,格式如下:

操作1: 1 x y z

操作2: 2 x y

操作3: 3 x z

操作4: 4 x

 

输出格式:

 

输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模)

 

输入输出样例

输入样例#1: 复制
5 5 2 24
7 3 7 8 0 
1 2
1 5
3 1
4 1
3 4 2
3 2 2
4 5
1 5 1 3
2 1 3
输出样例#1: 复制
2
21

说明

时空限制:1s,128M

数据规模:

对于30%的数据:N10,M10

对于70%的数据: N103,M103

对于100%的数据: N105,M105

( 其实,纯随机生成的树LCA+暴力是能过的,可是,你觉得可能是纯随机的么233 )

样例说明:

树的结构如下:

技术分享

各个操作如下:

技术分享

故输出应依次为2、21(重要的事情说三遍:记得取模)

赛前提高一下码题能力.

吐槽一下,写了1hour的树剖【吐血】.

链接:https://www.luogu.org/problemnew/show/3384

AC代码:

#include<cstdio>
#include<iostream>
#define FOR(i,s,t) for(register int i=s;i<=t;++i)
typedef long long ll;
const int maxn=100011;
int n,m,r,p,type,x,y;
ll z;
int a[maxn];
#define VIS(now) for(register int e=las[now];e;e=nxt[e])
namespace TCP_And_BIT{
	ll tr1[maxn],tr2[maxn],num[maxn];
	inline int lowbit(int x){
		return x&(-x);
	}
	inline void add(ll *r,int pos,ll x){
		for(;pos<=n;r[pos]+=x,r[pos]%=p,pos+=lowbit(pos));return;
	}
	inline void _add(int l,int r,ll x){
		x%=p;
		add(tr1,l,x);add(tr1,r+1,(-x+p)%p);
		add(tr2,l,1ll*(l-1)*x%p);add(tr2,r+1,1ll*(p-x)%p*r);
	}
	inline ll query(ll *r,int pos){
		ll ans=0;for(;pos;ans+=r[pos],ans%=p,pos-=lowbit(pos));return ans;
	}
	inline ll _query(int l,int r){
		ll sum1,sum2;
		sum1=1ll*(l-1)*query(tr1,l-1)-1ll*query(tr2,l-1);
		sum2=1ll*r*query(tr1,r)-1ll*query(tr2,r);
		return (sum2-sum1+2*p)%p;
	}
	int nxt[maxn<<1],las[maxn],to[maxn<<1];
	int dep[maxn],sz[maxn],f[maxn],top[maxn],xu[maxn];
	int tot,dfn;
	inline void insert(int x,int y){
		nxt[++tot]=las[x];las[x]=tot;to[tot]=y;return;
	}
	inline void dfs1(int now){
		sz[now]=1;
		VIS(now)
			if(!dep[to[e]]){
				dep[to[e]]=dep[now]+1;f[to[e]]=now;
				dfs1(to[e]);sz[now]+=sz[to[e]];
			}
		return;
	}
	inline void dfs2(int now,int chain){
		xu[now]=++dfn;
		num[dfn]=a[now];
		top[now]=chain;
		add(tr1,dfn,1ll*(num[dfn]-num[dfn-1]+p)%p);
		add(tr2,dfn,1ll*(dfn-1)%p*(num[dfn]-num[dfn-1]+p)%p);
		register int i=0;
		VIS(now)if(to[e]!=f[now]&&sz[i]<sz[to[e]])i=to[e];
		if(!i)return;
		dfs2(i,chain);
		VIS(now)if(to[e]!=f[now]&&i!=to[e])dfs2(to[e],to[e]);
		return;
	}
	inline ll query_sum(int x,int y){
		ll ans=0;
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]])x^=y^=x^=y;
			ans+=_query(xu[top[x]],xu[x]);ans=(ans+2*p)%p;x=f[top[x]];
		}
		if(dep[x]<dep[y])x^=y^=x^=y;
		ans+=_query(xu[y],xu[x]);ans=(ans+2*p)%p;
		return ans;
	}
	inline void add_num(int x,int y,ll v){
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]])x^=y^=x^=y;
			_add(xu[top[x]],xu[x],v);x=f[top[x]];
		}
		if(dep[x]<dep[y])x^=y^=x^=y;
		_add(xu[y],xu[x],v);return ;
	}
};
using namespace TCP_And_BIT;
int main(){
	scanf("%d%d%d%d",&n,&m,&r,&p);
	FOR(i,1,n)scanf("%d",a+i),a[i]%=p;
	FOR(i,2,n){
		scanf("%d%d",&x,&y);
		insert(x,y);insert(y,x);
	}
	dep[r]=1;
	dfs1(r);dfs2(r,r);
	while(m--){
		scanf("%d",&type);
		if(type==1){
			scanf("%d%d%lld",&x,&y,&z);
			z%=p;
			add_num(x,y,z);
		}
		if(type==2){
			scanf("%d%d",&x,&y);
			printf("%lld\n",(query_sum(x,y)+p)%p);
		}
		if(type==3){
			scanf("%d%lld",&x,&z);
			z%=p;
			_add(xu[x],xu[x]+sz[x]-1,z);
		}
		if(type==4){
			scanf("%d",&x);
			printf("%lld\n",(_query(xu[x],xu[x]+sz[x]-1)+p)%p);
		}
	}
	return 0;
}

  

以上是关于树链剖分套树状数组(区间修改)板子的主要内容,如果未能解决你的问题,请参考以下文章

POJ 2763 Housewife Wind(树链剖分+树状数组)

Codeforces Round #425 (Div. 2) D 树链剖分 + 树状数组维护区间

树链剖分

树链剖分

树链剖分详解

树链剖分+线段树 单点修改 区间求和 模板