树链剖分-模板

//P3384 【模板】轻重链剖分
//2020.11.10

//将树从x到y结点最短路径上所有节点的值都加上z
//求树从x到y结点最短路径上所有节点的值之和
//将以x为根节点的子树内所有节点值都加上z
//求以x为根节点的子树内所有节点值之和

#include <bits/stdc++.h>
#define ll long long
#define _for(i,a,b) for(ll i = (a);i < (b); ++i)
#define sc scanf
#define pr printf
#define TLE ios::sync_with_stdio(false); cin.tie(0);
const int maxn=200000 + 5;
const int INF = 0x3f3f3f3f;
using namespace std;

int depth[maxn];
int fa[maxn]; //father node
int sbs[maxn]; //subtree's node size
int son[maxn]; //每个非叶子节点的重儿子编号
int n,m,r,p; //n: 树的节点个数
             //m: 操作个数
             //r: 根节点序号
             //p: 取模数
int w[maxn]; //各节点权值
int id[maxn]; //标记每个点的新编号
int cnt = 0; //记录总编号数
int _w[maxn]; //新编号节点的权值
int top[maxn]; //节点所在链的最顶端节点
struct Edge{
    int t,nex;
}e[maxn];
int head[maxn] = {0},tot = 0;

inline void add(int u, int v) {
    e[++tot].t = v;
    e[tot].nex = head[u];
    head[u] = tot;
}

//线段树:
int tree[maxn<<2] = {0}, tag[maxn<<2] = {0};

void push_down(int node,int start,int end,int val) {
	tag[node] = (tag[node] + val) % p;
	tree[node] = (tree[node] + val * (end  - start + 1)) % p;
}

void build_tree(int node,int start,int end){
	if(start == end) {
		tree[node] = _w[start + 1];
	}
	else {
		int mid = start + end >> 1;
		int left_node  = (node << 1 ) + 1;
		int right_node = (node << 1 ) + 2;
		build_tree(left_node, start, mid);
		build_tree(right_node, mid + 1, end);
		tree[node] = (tree[left_node] + tree[right_node]);
	}
}

void updata_tree(int node,int start,int end,int L,int R,int val){
	//printf("start : %lld end : %lld\n",start,end); getchar();
	if(R < start || L > end) {
		return;
	}
	else if(start >= L && end <= R) {
		tree[node] += val * (end - start + 1) % p;
		tag[node] += val % p;
		return;
	}
	else {
		int mid = start + end >> 1;
		int left_node  = (node << 1 ) + 1;
		int right_node = (node << 1 ) + 2;
		if(tag[node] != 0) {
			push_down(left_node,  start, mid, tag[node]);
			push_down(right_node, mid+1, end, tag[node]);
			tag[node] = 0;
		}
		updata_tree(left_node,  start, mid, L, R, val);
		updata_tree(right_node, mid+1, end, L, R, val);
		tree[node] = (tree[left_node] + tree[right_node]) % p;
	}
}

int query_tree(int node,int start,int end,int L,int R){
	//printf("start : %lld end : %lld\n",start,end); getchar();
	if(R < start || L > end) {
		return 0;
	}
	else if(start >= L && end <= R) {
		return tree[node];
	}
	else {
		int mid = start + end >> 1;
		int left_node  = (node << 1 ) + 1;
		int right_node = (node << 1 ) + 2;
		if(tag[node] != 0) {
			push_down(left_node,  start, mid, tag[node]);
			push_down(right_node, mid+1, end, tag[node]);
			tag[node] = 0;
		}
		int left_sum  = query_tree(left_node,  start, mid, L, R) % p;
		int right_sum = query_tree(right_node, mid+1, end, L, R) % p;
		//printf("l : %lld r : %lld\n",left_sum,right_sum);
		return left_sum + right_sum;
	}
}

//标记每个节点的深度depth[]
//标记每个节点的父亲节点fa[]
//标记每个非叶子节点的子树大小(含自身节点)
//标记每个非叶子节点的重儿子编号son[]
inline void dfs1(int now, int fath) {
    depth[now] = depth[fath] + 1;
    fa[now] = fath;
    sbs[now] = 1;
    int mss = -1; //maxson size
    for(int i = head[now]; i; i = e[i].nex) if(e[i].t != fath) {
        int v = e[i].t;
        dfs1(v,now);
        sbs[now] += sbs[v];
        if(sbs[now] > mss) {
            mss = sbs[v];
            son[now] = v;
        }
    }
}

//标记每个点的新编号
//赋值每个点的初始值到新编号上
//处理每个点所在链的顶端
//处理每条链
inline void dfs2(int now, int topf) { //topf: 当前链的最顶端节点
    id[now] = ++cnt;
    _w[cnt] = w[now];
    top[now] = topf;
    if(!son[now]) return; //此节点没有重儿子,为叶子节点
    dfs2(son[now],topf);
    for(int i = head[now]; i; i = e[i].nex) if(e[i].t != fa[now] && e[i].t != son[now]) {
        dfs2(e[i].t, e[i].t);
    }
}

int res = 0;

//求树从x到y结点最短路径上所有节点的值之和
inline int qRange(int x,int y) {
    int ans = 0;
    while(top[x] != top[y]) {
        if(depth[top[x]] < depth[top[y]]) { //不妨设top[x]的深度 >= top[y]的深度
            swap(x,y);
        }
        ans += query_tree(0,0,n-1,id[top[x]]-1,id[x]-1); //x点到x所在链顶端这一区间段的点权和
        ans %= p;
        x = fa[top[x]];
    }
    if(depth[x] > depth[y]) swap(x,y);
    ans += query_tree(0,0,n-1,id[x]-1,id[y]-1) % p;
    ans %= p;
    return ans;
}

//求以x为根节点的子树内所有节点值之和
inline int qSon(int x) {
    return query_tree(0,0,n-1,id[x]-1,id[x]+sbs[x]-2) % p;
}

//将树从x到y结点最短路径上所有节点的值都加上z
inline void updRange(int x,int y,int k) {
    k %= p;
    while(top[x] != top[y]) {
        if(depth[top[x]] < depth[top[y]]) swap(x,y);
        updata_tree(0,0,n-1,id[top[x]]-1,id[x]-1,k);
        x = fa[top[x]];
    }
    if(depth[x] > depth[y]) swap(x,y);
    updata_tree(0,0,n-1,id[x]-1,id[y]-1,k);
}

//将以x为根节点的子树内所有节点值都加上z
inline void updSon(int x,int k) {
    k %= p;
    updata_tree(0,0,n-1,id[x]-1,id[x]+sbs[x]-2,k);
}

int main() {
    sc("%d %d %d %d",&n,&m,&r,&p);
    _for(i,1,n+1) {
        sc("%d",&w[i]);
    }
    int u,v;
    _for(i,1,n) {
        sc("%d %d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs1(r,0);
    dfs2(r,r);
    build_tree(0,0,n-1);
    while(m--) {
        int k,x,y,z;
        sc("%d",&k);
        if(k == 1) {
            sc("%d %d %d", &x, &y, &z);
            updRange(x,y,z);
        }
        else if(k == 2) {
            sc("%d %d", &x, &y);
            pr("%d\n", qRange(x,y));
        }
        else if(k == 3) {
            sc("%d %d", &x, &y);
            updSon(x,y);
        }
        else if(k == 4) {
            sc("%d",&x);
            pr("%d\n",qSon(x));
        }
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值