题目大意:

https://www.luogu.org/problemnew/show/P3384

树链剖分的讲解 两个dfs() 修改 查询

很详细很好理解 https://www.cnblogs.com/George1994/p/7821357.html

不过上面的讲解没有完整的代码

没有说到 操作3和操作4 应该处理的区间

某棵子树在线段树中的对应区间 可以从 根节点的dfsID p[i] 及 根节点的儿子数量 num[i] 得到

即若子树的根节点为 i 那么对应的区间应该是 ( p[i],p[i]+num[i]-1 )

BTW 其实这道题测试数据很弱 不一定能检验出模板的错误

#include <bits/stdc++.h>
using namespace std;
#define mem(i,j) memset(i,j,sizeof(i))
#define LL long long
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1 const int maxn = 2e5 + ;
const int maxnode = maxn<<;
const int maxedge = maxn<<; LL head[maxn], tot, pos;
LL fa[maxn], son[maxn], dep[maxn], num[maxn];
// i的父亲、i的重结点、i的深度、i的儿子个数
LL top[maxn], p[maxn], fp[maxn];
// i所在链的顶端、ID->dfsID、dfsID->ID
LL n,m,r,mod;
LL val[maxn];
struct Edge { int to,ne; }e[maxedge];
void init() {
tot=; pos=;
mem(head,);
mem(son,);
}
void add(int u,int v) {
e[tot].to = v;
e[tot].ne = head[u];
head[u] = tot++;
} struct IntervalTree {
LL _sum, _min, _max;
LL sumv[maxnode], minv[maxnode], maxv[maxnode], setv[maxnode], addv[maxnode];
void init() {
mem(sumv,); mem(setv,); mem(addv,);
} void maintain(int L, int R, int rt) {
int lc = rt<<, rc = rt<<|;
if(R > L) {
sumv[rt] = (sumv[lc] + sumv[rc])%mod;
minv[rt] = min(minv[lc], minv[rc]);
maxv[rt] = max(maxv[lc], maxv[rc]);
}
if(setv[rt] >= ) {
minv[rt] = maxv[rt] = setv[rt];
sumv[rt] = setv[rt] * (R-L+)%mod;
}
if(addv[rt]) {
minv[rt] += addv[rt];
maxv[rt] += addv[rt];
sumv[rt] = (sumv[rt]+addv[rt] * (R-L+)%mod)%mod;
}
} void pushdown(int rt) {
int lc = rt*, rc = rt*+;
if(setv[rt] >= ) {
setv[lc] = setv[rc] = setv[rt];
addv[lc] = addv[rc] = ;
setv[rt] = -;
}
if(addv[rt]) {
addv[lc] += addv[rt];
addv[rc] += addv[rt];
addv[rt] = ;
}
} ///update(更新区间左右端点、更新值、更新选项 op = 1 为加减 op != 1 为置值、当前区间左右端点、根)
void update(int L, int R, LL v, int op, int l, int r, int rt){
//int lc = rt<<1, rc = rt<<1|1;
if(L <= l && R >= r) {
if(op == ) addv[rt] += v;
else { setv[rt] = v; addv[rt] = ; }
} else {
pushdown(rt);
int m = l + (r-l)/;
if(L <= m) update(L, R, v, op, lson);
else maintain(lson);
if(R > m) update(L, R, v, op, rson);
else maintain(rson);
}
maintain(l, r, rt);
} ///query(问询的左右端点、累加lazy_tag的累加量、当前区间左右端点、根)
void query(int L, int R, LL add, int l, int r, int rt) {
if(setv[rt] >= ) {
LL v = setv[rt] + add + addv[rt];
_sum += v * (LL)(min(r,R)-max(l,L)+)%mod;
_min = min(_min, v);
_max = max(_max, v);
} else if(L <= l && R >= r) {
_sum += (sumv[rt] + add * (LL)(r-l+)%mod)%mod;
_min = min(_min, minv[rt] + add);
_max = max(_max, maxv[rt] + add);
} else {
int m = l + (r-l)/;
if(L <= m) query(L, R, add+addv[rt], lson);
if(R > m) query(L, R, add+addv[rt], rson);
}
}
}T; /** -----树链剖分----- */
void dfs1(int u,int pre,int d) {
dep[u]=d;
fa[u]=pre;
num[u]=;
for(int i=head[u];i;i=e[i].ne) {
int v=e[i].to;
if(v!=fa[u]) {
dfs1(v,u,d+);
num[u]+=num[v];
if(!son[u] || num[v]>num[son[u]])
son[u]=v;
}
}
}
void dfs2(int u,int sp) {
top[u]=sp;
p[u]=++pos;
fp[p[u]]=u;
if(!son[u]) return;
dfs2(son[u],sp);
for(int i=head[u];i;i=e[i].ne) {
int v=e[i].to;
if(v!=son[u] && v!=fa[u])
dfs2(v,v);
}
}
// 查询树上x到y的总和
LL queryPath(int x,int y) {
LL ans=0LL;
int fx=top[x], fy=top[y];
// fx==fy 说明到了LCA
while(fx!=fy) { // x y不在同一条重链上
if(dep[fx]>=dep[fy]) {
T._sum=0LL; T.query(p[fx],p[x],,,pos,);
ans=(ans+T._sum)%mod;
x=fa[fx];
} else {
T._sum=0LL; T.query(p[fy],p[y],,,pos,);
ans=(ans+T._sum)%mod;
y=fa[fy];
} // 先加离LCA更远的 且只加到父亲节点的一段 一步步移
fx=top[x], fy=top[y];
} // 直到两点在同一条重链上跳出 此时节点必连续 // 将最后到达LCA的一段连续的区间加上
if(p[x]>p[y]) swap(x,y);
T._sum=0LL; T.query(p[x],p[y],,,n,); return (ans+T._sum)%mod;
}
// 将树上x到y都加上z (和queryPath()差不多)
void updatePath(int x,int y,int z) {
int fx=top[x], fy=top[y];
while(fx!=fy) {
if(dep[fx]>=dep[fy]) {
T.update(p[fx],p[x],(LL)z,,,n,);
x=fa[fx];
} else {
T.update(p[fy],p[y],(LL)z,,,n,);
y=fa[fy];
}
fx=top[x], fy=top[y];
} if(p[x]>p[y]) swap(x,y);
T.update(p[x],p[y],(LL)z,,,n,);
}
/* ---------------- */ int main()
{
while(~scanf("%lld%lld%lld%lld",&n,&m,&r,&mod)) {
init();
for(int i=;i<=n;i++)
scanf("%lld",&val[i]);
for(int i=;i<n;i++) {
int a,b; scanf("%d%d",&a,&b);
add(a,b); add(b,a);
}
dfs1(r,,); // 根节点 前驱节点 深度
dfs2(r,r); // 当前节点 起始重结点
T.init();
for(int i=;i<=n;i++)
T.update(p[i],p[i],val[fp[p[i]]],,,n,); while(m--) {
int op,x,y,z;
scanf("%d",&op); //printf("op%d\n",op);
if(op==) {
scanf("%d%d%d",&x,&y,&z);
updatePath(x,y,z);
} else if(op==) {
scanf("%d%d",&x,&y);
printf("%lld\n",queryPath(x,y)%mod);
} else if(op==) {
scanf("%d%d",&x,&z);
T.update(p[x],p[x]+num[x]-,(LL)z,,,n,);
} else {
scanf("%d",&x);
T._sum=; T.query(p[x],p[x]+num[x]-,0LL,,n,);
printf("%lld\n",T._sum%mod);
}
}
} return ;
}

luogu3384 /// 树链剖分+线段树模板的更多相关文章

  1. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  2. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  3. B20J_2836_魔法树_树链剖分+线段树

    B20J_2836_魔法树_树链剖分+线段树 题意: 果树共有N个节点,其中节点0是根节点,每个节点u的父亲记为fa[u].初始时,这个果树的每个节点上都没有果子(即0个果子). Add u v d ...

  4. Aragorn's Story 树链剖分+线段树 && 树链剖分+树状数组

    Aragorn's Story 来源:http://www.fjutacm.com/Problem.jsp?pid=2710来源:http://acm.hdu.edu.cn/showproblem.p ...

  5. 【bzoj1782】[Usaco2010 Feb]slowdown 慢慢游 树链剖分+线段树

    题目描述 每天Farmer John的N头奶牛(1 <= N <= 100000,编号1…N)从粮仓走向他的自己的牧场.牧场构成了一棵树,粮仓在1号牧场.恰好有N-1条道路直接连接着牧场, ...

  6. 【bzoj1036】树的统计[ZJOI2008]树链剖分+线段树

    题目传送门:1036: [ZJOI2008]树的统计Count 这道题是我第一次打树剖的板子,虽然代码有点长,但是“打起来很爽”,而且整道题只花了不到1.5h+,还是一遍过样例!一次提交AC!(难道前 ...

  7. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  8. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  9. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  10. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

随机推荐

  1. Golang 标准库提供的Log(一)

      原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://gotaly.blog.51cto.com/8861157/1405754 G ...

  2. Ubuntu 16.04系统上修改Docker镜像的存储路径 (转)

    转自:https://blog.csdn.net/qihongchao/article/details/80651492 dba专门挂了一个硬盘让我放项目(测试环境)因为系统空间比较小,希望把dock ...

  3. Java-Class-C:java.util.BigDecimal

    ylbtech-Java-Class-C:java.util.BigDecimal 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部 1. /* * Copyright (c) 19 ...

  4. CSS:CSS 语法

    ylbtech-CSS:CSS 语法 1.返回顶部 1. CSS 语法 实例 查看 实例 1 查看 实例 2 CSS 实例 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明: 选择器通常是 ...

  5. Nginx 配置参数

    1 Proxy_send_timeout 定义后端在多久的时间内必须返回完所有的数据给Nginx. 2 Proxy_read_timeout

  6. MarkDown 快速开始 上手

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  7. opencv bwlabel

    int bwLabel(const Mat& imgBw, Mat& imgLabeled) { Mat imgClone = Mat(imgBw.rows + , imgBw.col ...

  8. PXE装机

    支持 centOS 6 支持 PXE 装机的网卡必须自带 ROM(非意识性存储器)存储内容包括:DHCP 客户端,TFTP 客户端,PXE 协议客户端,将网卡设置第一启动项. DHCP:自动分配 IP ...

  9. [已解决]windows安装docker的问题

    windows下载安装docker出现的问题 进入powershell后输入docker --version报错: could not read CA certificate "C:\\Us ...

  10. Python中使用gflags

    安装命令pip install python-gflags 使用示例: import gflags FLAGS = gflags.FLAGS gflags.DEFINE_string('name', ...