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

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%的数据:N≤10,M≤10

对于70%的数据: N≤103,M≤103

对于100%的数据: N≤105,M≤105

( 其实,纯随机生成的树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;
}

  

时间: 2024-11-05 21:47:24

树链剖分套树状数组(区间修改)板子的相关文章

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

【bzoj3779】重组病毒 LCT+树上倍增+DFS序+树状数组区间修改区间查询

题目描述 给出一棵n个节点的树,每一个节点开始有一个互不相同的颜色,初始根节点为1. 定义一次感染为:将指定的一个节点到根的链上的所有节点染成一种新的颜色,代价为这条链上不同颜色的数目. 现有m次操作,每次为一下三种之一: RELEASE x:对x执行一次感染: RECENTER x:把根节点改为x,并对原来的根节点执行一次感染: REQUEST x:询问x子树中所有节点感染代价的平均值. 输入 输入的第一行包含两个整数n和m,分别代表局域网中计算机的数量,以及操作和询问的总数.接下来n-1行,

【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改

题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. 输入 第一行N,M接下来M行,每行形如1 a b c或2 a b c 输出 输出每个询问的结果 样例输入 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 样例输出 1 2 1 题解 整体二分+树状数组区间修改 当年naive的树套树题解 前两天由于要

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

poj 2763 树链剖分(单点更新,区间求值)

http://poj.org/problem?id=2763 Description After their royal wedding, Jiajia and Wind hid away in XX Village, to enjoy their ordinary happy life. People in XX Village lived in beautiful huts. There are some pairs of huts connected by bidirectional ro

【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

题目描述 由乃正在做她的OJ.现在她在处理OJ上的用户排名问题.OJ上注册了n个用户,编号为1-",一开始他们按照编号 排名.由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天 天问她题...因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她 在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送 Deus:这个题怎么做呀? yuno:这个不是NOI2014的水题吗... Deu

树状数组 区间修改+区间查询

其实直到不久前都几乎不会用树状数组,请教了PPZ大佬之后终于懂了一点点. 然后小蒟蒻现在才知道了树状数组区间修改+区间查询的方法QAQ 传送门 Codevs 线段树练习3 //Twenty #include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<queu

【bzoj4540】[Hnoi2016]序列 单调栈+离线+扫描线+树状数组区间修改

题目描述 给出一个序列,多次询问一个区间的所有子区间最小值之和. 输入 输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数.接下来一行,包含n个整数,以空格隔开,第i个整数为ai,即序列第i个元素的值.接下来q行,每行包含两个整数l和r,代表一次询问. 输出 对于每次询问,输出一行,代表询问的答案. 样例输入 5 5 5 2 4 1 3 1 5 1 3 2 4 3 5 2 5 样例输出 28 17 11 11 17 题解 单调栈+离线+扫描线+树状数组区间修改 首先把使用单调栈找出每个

【bzoj5173】[Jsoi2014]矩形并 扫描线+二维树状数组区间修改区间查询

题目描述 JYY有N个平面坐标系中的矩形.每一个矩形的底边都平行于X轴,侧边平行于Y轴.第i个矩形的左下角坐标为(Xi,Yi),底边长为Ai,侧边长为Bi.现在JYY打算从这N个矩形中,随机选出两个不同的矩形,并计算它们的并的大小.JYY想知道,交的大小的期望是多少.换句话说即求在所有可能的选择中,两个矩形交的面积的平均大小是多大. 输入 输入一行包含一个正整数N. 接下来N行,每行4个整数,分别为Xi,Yi,Ai,Bi 2 < =  N < =  2*10^5, 0 < =  Xi,