zoukankan      html  css  js  c++  java
  • 【luogu P3384】【模板】轻重链剖分

    【模板】轻重链剖分

    题目链接:luogu P3384

    题目大意

    有一个树,点有权值,要你维护一些操作。
    把两点间的路径中的点的点权都加一个值或者询问和。
    把一个点对应的子树中点的点权都加一个值或者询问和。

    根给出,输出的和对一个给出的数取模。

    思路

    这道题也是树链剖分的模板题,之前写过一道,树链剖分怎么做就不写了。
    ——>点这里看<——

    但是这题比那一道有一个新的东西,就是会对一个点对于的子树进行操作。
    那这怎么办呢?
    我们考虑子树中的点在线段树中的位置,通过观察我们建线段树的过程。那你是 dfs 来标号的,那一个子树的点肯定是连续的一段,而且开头的是这个子树的根节点,也就是这给点。
    那它在线段树中就是一个区间,设这个子树根节点为 (root),大小(节点个数)为 (size_{root}),然后树中的点 (i) 在线段树中的位置是 (dy_i),那线段树对于它的区间就是 (dy_{root}sim dy_{root}+size_{root}-1)

    那就可以了。
    记得取模。

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    
    using namespace std;
    
    struct node {
    	int to, nxt;
    }e[200001];
    int n, x, y, z, le[100001], KK, tot, q, root, mo;
    int number[100001], top[100001], tree_pl[100001], normal_pl[400001];
    int fa[100001], num[100001], deg[100001], son[100001];
    long long sum[400001], Sum, lazy[400001];
    int op, size[100001];
    
    void add(int x, int y) {
    	e[++KK] = (node){y, le[x]}; le[x] = KK;
    }
    
    void dfs1(int now, int father) {
    	fa[now] = father;
    	deg[now] = deg[father] + 1;
    	size[now] = 1;
    	for (int i = le[now]; i; i = e[i].nxt)
    		if (father != e[i].to) {
    			dfs1(e[i].to, now);
    			size[now] += size[e[i].to];
    			if (size[e[i].to] > size[son[now]])
    				son[now] = e[i].to;
    		}
    }
    
    void dfs2(int now, int father) {
    	if (son[now]) {
    		tree_pl[son[now]] = ++tot;
    		top[son[now]] = top[now];
    		normal_pl[tree_pl[son[now]]] = son[now];
    		dfs2(son[now], now);
    	}
    	
    	for (int i = le[now]; i; i = e[i].nxt)
    		if (e[i].to != father && e[i].to != son[now]) {
    			tree_pl[e[i].to] = ++tot;
    			top[e[i].to] = e[i].to;
    			normal_pl[tree_pl[e[i].to]] = e[i].to;
    			dfs2(e[i].to, now);
    		}
    }
    
    void up(int now) {
    	sum[now] = sum[now << 1] + sum[now << 1 | 1];
    	sum[now] %= mo;
    }
    
    void down(int now, int l, int r) {
    	int mid = (l + r) >> 1;
    	sum[now << 1] += lazy[now] * (mid - l + 1) % mo;
    	sum[now << 1] %= mo;
    	sum[now << 1 | 1] += lazy[now] * (r - (mid + 1) + 1) % mo;
    	sum[now << 1 | 1] %= mo;
    	lazy[now << 1] += lazy[now];
    	lazy[now << 1] %= mo;
    	lazy[now << 1 | 1] += lazy[now];
    	lazy[now << 1 | 1] %= mo;
    	lazy[now] = 0;
    }
    
    void build(int now, int l, int r) {
    	if (l == r) {
    		sum[now] = number[normal_pl[l]] % mo;
    		return ;
    	}
    	int mid = (l + r) >> 1;
    	build(now << 1, l, mid);
    	build(now << 1 | 1, mid + 1, r);
    	up(now);
    }
    
    void add_num(int now, int l, int r, int L, int R, int add__num) {
    	if (l > R || r < L) return ;
    	if (l >= L && r <= R) {
    		sum[now] = (sum[now] + ((r - l + 1) * add__num) % mo) % mo;
    		if (l != r) lazy[now] += add__num;
    		return ;
    	}
    	down(now, l, r);
    	int mid = (l + r) >> 1;
    	if (L <= mid) add_num(now << 1, l, mid, L, R, add__num);
    	if (mid + 1 <= R)add_num(now << 1 | 1, mid + 1, r, L, R, add__num);
    	up(now);
    }
    
    void query(int now, int l, int r, int L, int R) {
    	if (r < L || l > R) return ;
    	if (l >= L && r <= R) {
    		Sum += sum[now];
    		Sum %= mo;
    		return ;
    	}
    	down(now, l, r);
    	int mid = (l + r) >> 1;
    	if (L <= mid) query(now << 1, l, mid, L, R);
    	if (mid + 1 <= R) query(now << 1 | 1, mid + 1, r, L, R);
    }
    
    void ask(int x, int y) {
    	while (top[x] != top[y]) {
    		if (deg[top[x]] < deg[top[y]]) {
    			swap(x, y);
    		}
    		query(1, 1, tot, tree_pl[top[x]], tree_pl[x]);
    		x = fa[top[x]];
    	}
    	if (deg[x] > deg[y]) swap(x, y);
    	query(1, 1, tot, tree_pl[x], tree_pl[y]);
    }
    
    void insert(int x, int y, int z) {
    	while (top[x] != top[y]) {
    		if (deg[top[x]] < deg[top[y]]) {
    			swap(x, y);
    		}
    		add_num(1, 1, tot, tree_pl[top[x]], tree_pl[x], z);
    		x = fa[top[x]];
    	}
    	if (deg[x] > deg[y]) swap(x, y);
    	add_num(1, 1, tot, tree_pl[x], tree_pl[y], z);
    }
    
    int main() {
    	scanf("%d %d %d %d", &n, &q, &root, &mo);
    	for (int i = 1; i <= n; i++) scanf("%d", &number[i]);
    	for (int i = 1; i < n; i++) {
    		scanf("%d %d", &x, &y);
    		add(x, y);
    		add(y, x);
    	}
    	
    	dfs1(root, 0);
    	tot = 1;
    	top[root] = root;
    	tree_pl[root] = 1;
    	normal_pl[1] = root;
    	dfs2(root, 0);
    	
    	build(1, 1, tot);
    	
    	for (int i = 1; i <= q; i++) {
    		scanf("%d", &op);
    		
    		if (op == 1) {
    			scanf("%d %d %d", &x, &y, &z);
    			insert(x, y, z);
    		}
    		else if (op == 2) {
    			scanf("%d %d", &x, &y);
    			Sum = 0;
    			ask(x, y);
    			printf("%lld
    ", Sum);
    		}
    		else if (op == 3) {
    			scanf("%d %d", &x, &y);
    			add_num(1, 1, tot, tree_pl[x], tree_pl[x] + size[x] - 1, y);
    			//某个点对应子树在线段树中的位置=它在线段树中的位置~它的位置+它子树的节点个数-1
    			//下面同理
    		}
    		else if (op == 4) {
    			scanf("%d", &x);
    			Sum = 0;
    			query(1, 1, tot, tree_pl[x], tree_pl[x] + size[x] - 1);
    			printf("%lld
    ", Sum);
    		}
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    Windows Server 2012配置开机启动项
    Windows Server 2019 SSH Server
    NOIP2017 senior A 模拟赛 7.7 T1 棋盘
    Noip 2015 senior 复赛 Day2 子串
    Noip 2015 senior复赛 题解
    Noip 2014 senior Day2 解方程(equation)
    Noip 2014 senior Day2 寻找道路(road)
    Noip 2014 senior Day2 无线网络发射器选址(wireless)
    Noip2014senior复赛 飞扬的小鸟
    Noip 2014 senior 复赛 联合权值(link)
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P3384.html
Copyright © 2011-2022 走看看