洛谷p3384【模板】树链剖分题解
洛谷p3384 【模板】树链剖分错误记录
首先感谢\(lfd\)在课上调了出来\(Orz\)
\(1\).以后少写全局变量
\(2\).线段树递归的时候最好把左右区间一起传
\(3\).写\(dfs\)的时候不要写错名字
\(4\).使用线段树的操作的时候才要用到\(dfs\)序
\(5\).需要开一个数组来记录在\(dfs\)序下的节点是什么也方便线段树的赋值
\(6\).注意\(down\)函数内怎样更新
\(7\).在查询的时候并不需要向上更新
由于\(yxj\)看了\(lfd\)敲的树链剖分感觉压完行之后非常的好看,由此\(yxj\)也踏上了疯狂压行的不归路
Code:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lson k << 1
#define rson k << 1 | 1
using namespace std;
const int N = 1e5+7;
int n, m, R, p, dfn[N], top[N], son[N], dep[N], fa[N], siz[N], tot, head[N << 1], cnt, num, x, y, z, w[N], l, r, ans, pre[N];
struct node {int l, r, f, w;}tr[N << 2];
struct Node {int nxt, to;}e[N << 1];
int read() {
int s = 0, w = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') w = -1; ch = getchar();}
while(isdigit(ch)) {s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
void build(int k, int l, int r) {
tr[k].l = l, tr[k].r = r;
if(l == r) {tr[k].w = w[pre[l]]; return;}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
tr[k].w = (tr[lson].w + tr[rson].w) % p;
}
void add(int x, int y) {
e[++cnt].nxt = head[x];
e[cnt].to = y;
head[x] = cnt;
}
void dfs(int x) {
siz[x] = 1;
dep[x] = dep[fa[x]] + 1;
for(int i = head[x]; i; i = e[i].nxt) {
if(e[i].to == fa[x]) continue;
fa[e[i].to] = x, dfs(e[i].to), siz[x] += siz[e[i].to];
if(siz[e[i].to] > siz[son[x]]) son[x] = e[i].to;
}
}
void dfs1(int x) {
dfn[x] = ++tot; pre[tot] = x;
if(!top[x]) top[x] = x;
if(son[x]) top[son[x]] = top[x], dfs1(son[x]);
for(int i = head[x]; i; i = e[i].nxt)
if(e[i].to != fa[x] && e[i].to != son[x]) dfs1(e[i].to);
}
void down(int k) {
tr[lson].f += tr[k].f; tr[rson].f += tr[k].f;
tr[lson].w += (tr[lson].r - tr[lson].l + 1) * tr[k].f;
tr[rson].w += (tr[rson].r - tr[rson].l + 1) * tr[k].f;
tr[k].f = 0;
}
void change_query(int k) {
if(tr[k].l >= l && tr[k].r <= r) {tr[k].w += (tr[k].r - tr[k].l + 1) * z;tr[k].f += z; return;}
if(tr[k].f) down(k);
int mid = (tr[k].l + tr[k].r) >> 1;
if(l <= mid) change_query(lson);
if(r > mid) change_query(rson);
tr[k].w = (tr[lson].w + tr[rson].w) % p;
}
void work(int x, int y) {
z %= p;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
l = dfn[top[x]], r = dfn[x], change_query(1);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
l = dfn[x], r = dfn[y]; change_query(1);
}
void ask_query(int k) {
if(tr[k].l >= l && tr[k].r <= r) {ans = (ans + tr[k].w) % p; return;}
if(tr[k].f) down(k);
int mid = (tr[k].l + tr[k].r) >> 1;
if(l <= mid) ask_query(lson);
if(r > mid) ask_query(rson);
tr[k].w = (tr[lson].w + tr[rson].w) % p;
}
void work1(int x, int y) {
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
l = dfn[top[x]], r = dfn[x]; ask_query(1);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
l = dfn[x]; r = dfn[y]; ask_query(1);
}
int main() {
n = read(), m = read(), R = read(), p = read();
for(int i = 1; i <= n; i++) w[i] = read();
for(int i = 1; i < n; i++) x = read(), y = read(), add(x, y), add(y, x);
dfs(R); dfs1(R); build(1, 1, n);
while(m--) {
num = read();
if(num == 1) cin >> x >> y >> z, work(x, y);
if(num == 2) ans = 0, cin >> x >> y, work1(x, y), cout << ans << endl;
if(num == 3) cin >> x >> z, l = dfn[x], r = dfn[x] + siz[x] - 1, change_query(1);
if(num == 4) ans = 0, cin >> x, l = dfn[x], r = dfn[x] + siz[x] - 1, ask_query(1), cout << ans << endl;
}
return 0;
}
谢谢收看,祝身体健康!
洛谷p3384【模板】树链剖分题解的更多相关文章
- [洛谷P3384] [模板] 树链剖分
题目传送门 显然是一道模板题. 然而索引出现了错误,狂wa不止. 感谢神犇Dr_J指正.%%%orz. 建线段树的时候,第44行. 把sum[p]=bv[pos[l]]%mod;打成了sum[p]=b ...
- [luogu P3384] [模板]树链剖分
[luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...
- 洛谷P3979 遥远的国度 树链剖分+分类讨论
题意:给出一棵树,这棵树每个点有权值,然后有3种操作.操作一:修改树根为rt,操作二:修改u到v路径上点权值为w,操作三:询问以rt为根x子树的最小权值. 解法:如果没有修改树根操作那么这题就是树链剖 ...
- 洛谷 P4114 Qtree1 树链剖分
目录 题面 题目链接 题目描述 输入输出格式 输入格式: 输出格式: 输入输出样例 输入样例: 输出样例: 说明 说明 思路 Change Query AC代码 总结 题面 题目链接 P4114 Qt ...
- 洛谷.4114.Qtree1(树链剖分)
题目链接 模板题都错了这么多次.. //边权赋到点上 树剖模板 //注意LCA.链的顶端不能统计到答案! #include <cstdio> #include <cctype> ...
- 洛谷3384&bzoj1036树链剖分
值得注意的是: 一个点的子树是存在一起的...也就是说我们修改子树的时候只用... /********************************************************* ...
- P3384 [模板] 树链剖分
#include <bits/stdc++.h> using namespace std; typedef long long ll; int n, m, rt, mod, cnt, to ...
- luoguP3384 [模板]树链剖分
luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #inc ...
- 洛谷P2590 [ZJOI2008]树的统计 题解 树链剖分+线段树
题目链接:https://www.luogu.org/problem/P2590 树链剖分模板题. 剖分过程要用到如下7个值: fa[u]:u的父节点编号: dep[u]:u的深度: size[u]: ...
随机推荐
- Uboot启动流程分析(一)
1.前言 Linux系统的启动需要一个bootloader程序,该bootloader程序会先初始化DDR等外设,然后将Linux内核从flash中拷贝到DDR中,最后启动Linux内核,uboot的 ...
- Course: ISA 414
Assignment #4Course: ISA 414Points:100Due date: November 18th, 2019, before 11:59 pmSubmission instr ...
- 什么是IDE(集成开发环境)?
实际开发中,除了编译器是必须的工具,我们往往还需要很多其他辅助软件,例如: 编辑器:用来编写代码,并且给代码着色,以方便阅读: 代码提示器:输入部分代码,即可提示全部代码,加速代码的编写过程: 调试器 ...
- opencv 图像旋转
理论 http://www.cnblogs.com/wangguchangqing/p/4045150.html 翻开任意一本图像处理的书,都会讲到图像的几何变换,这里面包括:仿射变换(affine ...
- 【JS】---5 JS通过事件隐藏显示元素
JS通过事件隐藏显示元素 在开发中,很多时候我们需要点击事件,才显示隐藏元素.那如何做到页面刚开始就把标签隐藏. 有两种方法: (1) display:none <div id=" ...
- 程序基于InstallShield2013LimitedEdition的安装和部署
在VS2012之前,我们做安装包一般都是使用VS自带的安装包制作工具来创建安装包的,VS2012.VS2013以后,微软把这个去掉,集成使用了InstallShield进行安装包的制作了,虽然思路差不 ...
- java如何实现webservice中wsdlLocation访问地址的可配置化
背景:项目中调用了别的系统的webservice接口,调用成功之后发现wsdlLocation的地址是写死的,不方便修改,所以需要实现地址,包括用户名密码的可配置.项目的框架是Spring,调用web ...
- Windows 10 Java开发环境配置
一.JDK下载 安装java开发环境,第一步就是下载jdk安装包.打开浏览器进入oracle官网下载.这里注意jdk和jre的区别,jdk(java develop environment)是java ...
- 汇编指令之CMP, TEST指令
一.CMP指令 这一块呢,我不想上图了,汇编的博文我已经快要让我写吐了,其实也有好多我没有补充进来,比如进制,LEA指令,数据宽度,有符号,无符号的区分等等,但我真的要吐了,这些玩意我已经不是第一次写 ...
- 动态改变伪元素样式的方(用:after和:before生成的元素)
自己查资料总结的两种方法 一.纯css改变 a:hover:before{left:-20%;} a:hover:after{right:-20%;} a:before{ left:-100%; } ...