1. 程式人生 > 實用技巧 >樹鏈剖分筆記(輕重鏈剖分)

樹鏈剖分筆記(輕重鏈剖分)

我今天居然一次提交就A了!驚喜!

樹鏈剖分可以在樹上維護點的權值,可以進行一條鏈上的求和和修改操作,也可以將一個子樹進行求和和修改。

我們的具體做法是要將這個樹放到一個序列裡,然後使用線段樹來維護它。

我們定義重鏈和輕鏈,重鏈是指一個節點連向它最重的子節點的邊,我們可以發現我們將相連的重鏈看做一條重鏈。

我們可以將一顆樹拆分成輕鏈和重鏈,優先dfs重鏈,可以使得一條重鏈的dfs序是連續的,所以重鏈線上段樹上也是一段連續的區間。

我們來找兩個點的最近公共祖先,通過重鏈來向上跳,可以找到兩個點的LCA,由於跳的都是重鏈,可以線段樹區間維護一下。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define mid (l+r>>1)
#define B printf("!");
using namespace std;

int read()
{
	int a = 0,x = 1;
	char ch = getchar();
	while(ch > '9' || ch < '0'){
		if(ch == '-') x = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		a = a*10 + ch-'0';
		ch = getchar();
	}
	return a*x;
}
const int N=1e6+7;
int n,m,r,p;
int arr[N];

int head[N],go[N],nxt[N],cnt;
void add(int u,int v)
{
	go[++cnt] = v;
	nxt[cnt] = head[u];
	head[u] = cnt;
}
int siz[N],son[N],dfn[N],dis[N],str[N],pos[N],fa[N];

void dfs1(int u)
{
	siz[u] = 1;
	for(int e = head[u];e;e = nxt[e]){
		int v = go[e];
		if(v == fa[u]) continue;
		dis[v] = dis[u] + 1;
		fa[v] = u;
		dfs1(v);
		if(siz[v] > siz[son[u]]) son[u] = v;
		siz[u] += siz[v];
	}
}

void dfs2(int u,int h)
{
	str[u] = h;
	dfn[u] = ++cnt;
	pos[cnt] = u;
	if(son[u]) dfs2(son[u],h);
	for(int e = head[u];e;e = nxt[e]){
		int v = go[e];
		if(v == fa[u] || v == son[u]) continue;
		dfs2(v,v);
	}
}

int lazy[N],tre[N];

void build(int root,int l,int r)
{
	if(l == r) {tre[root] = arr[pos[l]];return;}
	build(root<<1,l,mid);build(root<<1|1,mid+1,r);
	tre[root] = (tre[root<<1]+tre[root<<1|1]) % p;
}

void push_down(int root,int l,int r)
{
	(tre[root<<1] += (mid-l+1)*lazy[root]%p)%=p,(tre[root<<1|1] += (r-mid)*lazy[root]%p)%=p;
	(lazy[root<<1] += lazy[root])%=p,(lazy[root<<1|1] += lazy[root])%=p;
	lazy[root] = 0;
}

void modify(int root,int l,int r,int ql,int qr,int x)
{
	if(l >= ql && r <= qr) {(tre[root] += (r-l+1)*x%p)%=p,(lazy[root] += x)%=p;return;}
	if(l > qr || r < ql) return;
	push_down(root,l,r);
	modify(root<<1,l,mid,ql,qr,x);modify(root<<1|1,mid+1,r,ql,qr,x);
	tre[root] = (tre[root<<1] + tre[root<<1|1])%p;
}

int query(int root,int l,int r,int ql,int qr)
{
	if(l >= ql && r <= qr) return tre[root];
	if(l > qr || r < ql) return 0;
	push_down(root,l,r);
	return (query(root<<1,l,mid,ql,qr)+query(root<<1|1,mid+1,r,ql,qr))%p;
}


void LCA_updata(int u,int v,int x)
{
	while(str[u] != str[v]){
//		printf("%d %d\n",u,v);
		if(dis[str[u]] < dis[str[v]]) swap(u,v);
		modify(1,1,n,dfn[str[u]],dfn[u],x);
		u = fa[str[u]];
	}
	if(dis[u] > dis[v]) swap(u,v);
	modify(1,1,n,dfn[u],dfn[v],x);
}

void LCA_query(int u,int v)
{
	int ret = 0;
	while(str[u] != str[v]){
		if(dis[str[u]] < dis[str[v]]) swap(u,v);
		(ret += query(1,1,n,dfn[str[u]],dfn[u])) %= p;
		u = fa[str[u]];
	}
	if(dis[u] > dis[v]) swap(u,v);
	(ret += query(1,1,n,dfn[u],dfn[v])) %= p;
	printf("%d\n",ret);
}

void tre_updata(int u,int x)
{
	modify(1,1,n,dfn[u],dfn[u]+siz[u]-1,x);
}

void tre_query(int u)
{
	int ret = query(1,1,n,dfn[u],dfn[u]+siz[u]-1);
	printf("%d\n",ret);
}
int main()
{
//	freopen("in.in","r",stdin);
//	freopen("out.out","w",stdout);
	n = read(),m = read(),r = read(),p = read();
	for(int i = 1;i <= n;i ++) arr[i] = read();
	for(int i = 1;i < n;i ++){
		int u = read(),v = read();
		add(u,v);add(v,u);
	}
	cnt = 0;
	dfs1(r);dfs2(r,r);

	build(1,1,n);

	for(int i = 1;i <= m;i ++){
		int op = read();
		if(op == 1){
			int x = read(),y = read(),z = read();
			LCA_updata(x,y,z);
		} else if(op == 2) {
			int x = read(),y = read();
			LCA_query(x,y);
		} else if(op == 3) {
			int x = read(),z = read();
			tre_updata(x,z);
		} else if(op == 4) {
			int x = read();
			tre_query(x);
		}
	}
	return 0;
}