1. 程式人生 > >樹鏈剖分套樹狀數組(區間修改)板子

樹鏈剖分套樹狀數組(區間修改)板子

c代碼 msu style ont 重要 pri ott src inline

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;
}

  

樹鏈剖分套樹狀數組(區間修改)板子