Problem Portal

Portal1: Luogu

Description

如题,已知一棵包含\(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为根节点的子树内所有节点值之和。

Input

第一行包含\(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

Output

输出包含若干行,分别依次表示每个操作\(2\)或操作\(4\)所得的结果(对\(P\)取模)。

Sample Input

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

Sample Output

2
21

Solution

模板树链剖分题。

一些概念:

  • 重儿子:在每一个非叶子结点的儿子中,以那个儿子结点为根的子树的结点数最多的儿子为该结点的重儿子;

  • 轻儿子:在非叶子,非重儿子结点;

  • 重边:一个父亲结点连结它的重儿子的边;

  • 轻边:非重边;

  • 重链:相邻重边连起来的,连接一条重儿子结点的链叫重链。

dfs1的功能:

  • 求出每结点的深度;

  • 求出每个结点的父亲节点;

  • 求出每个非叶子结点的子树的大小;

  • 求出每个非叶子结点的重儿子的编号。

dfs2的功能:

  • 处理每条链;

  • 标记每个结点的新编号;

  • 求出每个结点所在链的顶;

  • 把结点的初始值更新到新编号里。

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath> using namespace std; const int MAXN = 2000005;
struct EDGE {
int u, v, nxt;
} edge[MAXN];
struct node {
int l, r, w, size, f;
} tree[MAXN];
int n, m, root, mod, cnt, num = 1, a[MAXN], b[MAXN], tot[MAXN], son[MAXN], top[MAXN], idx[MAXN], dep[MAXN], head[MAXN], father[MAXN];
inline void addedge(int u, int v) {
edge[num].u = u; edge[num].v = v; edge[num].nxt = head[u]; head[u] = num++;
}
//dep[i]表示i结点的深度
//father[i]表示i结点的父亲结点
//son[]表示重儿子的编号
inline int dfs1(int now, int f, int deep) {
dep[now] = deep;
father[now] = f;
tot[now] = 1;
int Maxson = -1;
for (int i = head[now]; ~i; i = edge[i].nxt) {
if (edge[i].v == f) continue;
tot[now] += dfs1(edge[i].v, now, deep + 1);
if (tot[edge[i].v] > Maxson) {
Maxson = tot[edge[i].v];
son[now] = edge[i].v;
}
}
return tot[now];
}
inline void dfs2(int now, int topf) {
idx[now] = ++cnt;
a[cnt] = b[now];
top[now] = topf;
if (!son[now]) return ;
dfs2(son[now], topf);
for (int i = head[now]; ~i; i = edge[i].nxt)
if (!idx[edge[i].v]) dfs2(edge[i].v, edge[i].v);
}
inline void pushup(int root) {
tree[root].w = (tree[root << 1].w + tree[root << 1 | 1].w + mod) % mod;
}
inline void build(int root, int l, int r) {
tree[root].l = l; tree[root].r = r; tree[root].size = r - l + 1;
if (l == r) {
tree[root].w = a[l];
return ;
}
int mid = l + r >> 1;
build(root << 1, l, mid);
build(root << 1 | 1, mid + 1, r);
pushup(root);
}
inline void pushdown(int root) {
if (!tree[root].f) return ;
tree[root << 1].w = (tree[root << 1].w + tree[root << 1].size * tree[root].f) % mod;
tree[root << 1 | 1].w = (tree[root << 1 | 1].w + tree[root << 1 | 1].size * tree[root].f) % mod;
tree[root << 1].f = (tree[root << 1].f + tree[root].f) % mod;
tree[root << 1 | 1].f = (tree[root << 1 | 1].f + tree[root].f) % mod;
tree[root].f = 0;
}
inline void update_add(int root, int ansl, int ansr, int val) {
if (ansl <= tree[root].l && tree[root].r <= ansr) {
tree[root].w += tree[root].size * val;
tree[root].f += val;
return ;
}
pushdown(root);
int mid = tree[root].l + tree[root].r >> 1;
if (ansl <= mid) update_add(root << 1, ansl, ansr, val);
if (ansr > mid) update_add(root << 1 | 1, ansl, ansr, val);
pushup(root);
}
//线段树操作
inline void tree_add(int x, int y, int val) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
update_add(1, idx[top[x]], idx[x], val);
x = father[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
update_add(1, idx[x], idx[y], val);
}
inline int query_sum(int root, int ansl, int ansr) {
int ret = 0;
if (ansl <= tree[root].l && tree[root].r <= ansr) return tree[root].w;
pushdown(root);
int mid = tree[root].l + tree[root].r >> 1;
if (ansl <= mid) ret = (ret + query_sum(root << 1, ansl, ansr)) % mod;
if (ansr > mid) ret = (ret + query_sum(root << 1 | 1, ansl, ansr)) % mod;
return ret;
}
inline void tree_sum(int x, int y) {
int ret = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
ret = (ret + query_sum(1, idx[top[x]], idx[x])) % mod;
x = father[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
ret = (ret + query_sum(1, idx[x], idx[y])) % mod;
printf("%d\n", ret);
}
int main() {
memset(head, -1, sizeof(head));
scanf("%d%d%d%d", &n, &m, &root, &mod);
for (int i = 1; i <= n; i++)
scanf("%d", &b[i]);
for (int i = 1; i < n; i++) {
int x, y;
scanf("%d%d", &x, &y);
addedge(x, y);
addedge(y, x);
}
dfs1(root, 0, 1);
dfs2(root, root);
build(1, 1, n);
while (m--) {
int opt, x, y, val;
scanf("%d", &opt);
if (opt == 1) {
scanf("%d%d%d", &x, &y, &val);
val %= mod;
tree_add(x, y, val);
} else
if (opt == 2) {
scanf("%d%d", &x, &y);
tree_sum(x, y);
} else
if (opt == 3) {
scanf("%d%d", &x, &val);
update_add(1, idx[x], idx[x] + tot[x] - 1, val % mod);
} else {
scanf("%d", &x);
printf("%d\n", query_sum(1, idx[x], idx[x] + tot[x] - 1));
}
}
return 0;
}

『题解』洛谷P3384 【模板】树链剖分的更多相关文章

  1. [洛谷P3384] [模板] 树链剖分

    题目传送门 显然是一道模板题. 然而索引出现了错误,狂wa不止. 感谢神犇Dr_J指正.%%%orz. 建线段树的时候,第44行. 把sum[p]=bv[pos[l]]%mod;打成了sum[p]=b ...

  2. 『题解』洛谷P1063 能量项链

    原文地址 Problem Portal Portal1:Luogu Portal2:LibreOJ Portal3:Vijos Description 在\(Mars\)星球上,每个\(Mars\)人 ...

  3. [luogu P3384] [模板]树链剖分

    [luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...

  4. 洛谷P3979 遥远的国度 树链剖分+分类讨论

    题意:给出一棵树,这棵树每个点有权值,然后有3种操作.操作一:修改树根为rt,操作二:修改u到v路径上点权值为w,操作三:询问以rt为根x子树的最小权值. 解法:如果没有修改树根操作那么这题就是树链剖 ...

  5. 洛谷 P4114 Qtree1 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式: 输出格式: 输入输出样例 输入样例: 输出样例: 说明 说明 思路 Change Query AC代码 总结 题面 题目链接 P4114 Qt ...

  6. 『题解』洛谷P3376 【模板】网络最大流

    Problem Portal Portal1:Luogu Description 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流. Input 第一行包含四个正整数\(N,M,S,T\),分 ...

  7. 『题解』洛谷P1351 联合权值

    更好的阅读体验 Portal Portal1: Luogu Portal2: LibreOJ Description 无向连通图\(\mathrm G\)有\(n\)个点,\(n - 1\)条边.点从 ...

  8. 『题解』洛谷P2357 守墓人

    Portal Portal1: Luogu Description 在一个荒凉的墓地上有一个令人尊敬的守墓人,他看守的墓地从来没有被盗过, 所以人们很放心的把自己的先人的墓安顿在他那守墓人能看好这片墓 ...

  9. 『题解』洛谷P1993 小K的农场

    更好的阅读体验 Portal Portal1: Luogu Description 小\(K\)在\(\mathrm MC\)里面建立很多很多的农场,总共\(n\)个,以至于他自己都忘记了每个农场中种 ...

随机推荐

  1. 多线程EventWaitHandle -戈多编程

    在.NET的System.Threading命名空间中有一个名叫WaitHandler的类,这是一个抽象类(abstract),我们无法手动去创建它,但是WaitHandler有三个子类,这三个子类分 ...

  2. Spring Boot 定时任务 @Scheduled

    项目开发中经常需要执行一些定时任务,比如在每天凌晨,需要从 implala 数据库拉取产品功能活跃数据,分析处理后存入到 MySQL 数据库中.类似这样的需求还有许多,那么怎么去实现定时任务呢,有以下 ...

  3. 美化你的IDEA—背景图片

    IDEA设置背景图片 很多人都不知道IDEA可以像桌面一样设置背景图片,下面我们来美化我们的开发工具. 有的IDEA版本是搜不到的,我这个就是,现在搜的是已经装好的. 没有的我们可以去http://p ...

  4. (图解)windows下自定义Eclipse主题

    之前用过一些编辑器如SublimeText,IDE如phpstorm,IDEA等:这些工具给我的感觉就是,除了给予这些工具应有的功能外,给予开发者更友好,更舒服的界面.Eclipse很早就开始使用了, ...

  5. 快速入门Maven(二)(Eclipse构建Maven项目)

    Mars2的eclipse()已经集成了Maven插件,所以用这个版本不需要装插件了. 接下来构建: 一.调整Eclipse设置 1.选择3.3.9版本的maven软件 2.修改默认的本地仓库地址 二 ...

  6. Python3_基础

    目录 数据类型 变量 数据类型的转换 算术操作符 输入 字符串常用方法 数据类型 我们先来看看三种常见的数据类型 字符串 str 在Python中,字符串一般都用引号引起来,不管是用单引号还是双引号都 ...

  7. java控制执行流程

    控制执行流程 欢迎转载,转载烦请注明出处,谢谢. https://www.cnblogs.com/sx-wuyj/p/11177257.html java当中涉及到的关键字包括if-else.whil ...

  8. CentOS6.5下搭建FTP服务

    一.FTP协议 FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一.FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端.其中FT ...

  9. [.Net Core 3.0从入门到精通]1.笔记简介及.Net Core3.0介绍

    文章目的:.Net Core 3.0学习笔记整理与分享. 面向人群:有一定基础的C#开发人员或学习人员(C#语法一定要掌握). 笔者水平:中级C#开发攻城狮(水平有限,写的不对的地方希望大家指正). ...

  10. 常用 UML 类图

    一. 类 类图分三层: 第一层是类的名称,如果是抽象类,则用斜体显示. 第二层是类的特性,通常就是字段和属性. 第三层是类的操作,通常是方法或行为.注意 '+' 表示 public,'-' 表示 pr ...