P4315 月下“毛景树”

  • 题目大意
  • 题目链接
  • 分析
  • 代码
    • 准备阶段
    • dfs
    • 线段树
      • 核心操作
      • 建树
      • 实现区间加
      • 实现区间和单点修改
      • 区间最大值查询
    • 树上操作
    • 主函数
  • 完整代码
    • 测试样例

题目大意

  1. Change k w:将第k条树枝边的边权改变为w。

  2. Cover u v w:将节点u与节点v之间的边上的边权全改变为w。(w不会为负值)

  3. Add u v w:将节点u与节点v之间的树枝上边的边权都增加w。

  4. Max u v:询问节点u与节点v之间边权。

题目链接

传送门

分析

可以很快意识到这是一题树链剖分题,不过和传统的树链剖分不一样,本题的权值是附在边上而不是节点上的。于是我们可以考虑在dfs的过程中将边权转化在点权上。由于父节点和它的子节点是一对多的关系,所以只能是将father -> son这条边的边权附在son上,否则若附在fahter上,fahter有极大可能会被重复赋值。这显然不是我们希望看见的。
我们还需要两种修改操作,因此需要两个lazytag,细节很多,详见代码。

代码

准备阶段

//变量声明
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int n, tot, cnt;
int head[N];
//深度,大小,父节点,重儿子,点的赋值
int dep[N], size[N],par[N], son[N], w[N];
//时间戳,对应的反函数,链顶
int dfn[N], pre[N], top[N];
//快读和链式前向星加边
inline int read(){int x = 0, op = 1;char ch = getchar();while (!isdigit(ch)){if (ch == '-') op = -1;ch = getchar();}while (isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}return x * op;
}inline void add_edge(int u, int v, int w){e[++tot] = {u, v, head[u], w};head[u] = tot;
}

dfs

//在第一次dfs完成点的赋值
void dfs1(int u, int fa){size[u] = 1;dep[u] = dep[fa] + 1;par[u] = fa;for (int i = head[u];i; i = e[i].nxt) {int v = e[i].to;if (v == fa) continue;w[v] = e[i].val;dfs1(v, u);size[u] += size[v];if (size[v] > size[son[u]])son[u] = v;}
}void dfs2(int u, int tp){top[u] = tp;dfn[u] = ++cnt;pre[cnt] = u;if (son[u])dfs2(son[u], tp);for (int i = head[u]; i; i = e[i].nxt) {int v = e[i].to;if (v != par[u] && v != son[u])dfs2(v, v);}
}

线段树

核心操作

线段树要完成操作:

  1. 区间加 add_interval
  2. 区间修改 modify_interval
  3. 单点修改 modify_point
  4. 区间最值访问 query_max

对1我们用懒标签add,对2和3用懒标签tag
想象一下对一个区间,如果先add再modify,区间修改的效果会覆盖区间加,所以可以确定优先级是tag比add大,也就是说tag会破坏掉add。我们把tag初始化为-1,add初始化为0.如果出现tag和add同时不为初始值的情况,一定是先区间修改,再区间加。因此可以确定关键操作push_down

int Max[N << 2], add[N << 2], tag[N << 2];inline void push_up(int i){Max[i] = max(Max[i << 1], Max[i << 1|1]);
}inline void push_down(int i){//如果tag和add都不为初始值,一定是先tag再addif (tag[i] != -1){add[i << 1] = add[i << 1|1] = 0;tag[i << 1] = tag[i <<1|1] = tag[i];Max[i << 1] = Max[i << 1|1] = tag[i];tag[i] = -1;}if (add[i] == 0) return;add[i << 1] += add[i];add[i << 1|1] += add[i];Max[i << 1] += add[i];Max[i << 1|1] += add[i];add[i] = 0;
}

建树

//tag的初始化在主函数中用memset实现
inline void buildTree(int i, int l, int r){if (l == r){Max[i] = w[pre[l]];return;}buildTree(i << 1, l, mid);buildTree(i << 1|1, mid + 1, r);push_up(i);
}

实现区间加

inline void add_interval(int i, int l, int r, int crtl, int crtr, int val){if (l > crtr || r < crtl)return;if (l >= crtl && r <= crtr){add[i] += val;Max[i] += val;return;}push_down(i);if (mid >= crtl)add_interval(i << 1, l, mid, crtl, crtr, val);if (mid < crtr)add_interval(i << 1|1, mid + 1, r, crtl, crtr, val);push_up(i);
}

实现区间和单点修改

inline void modify_interval(int i, int l, int r, int crtl, int crtr, int val){if (l > crtr || r < crtl)return;if (l >= crtl && r <= crtr){tag[i] = val;Max[i] = val;//tag会破坏掉addadd[i] = 0;return;}push_down(i);if (mid >= crtl)modify_interval(i << 1, l, mid, crtl, crtr, val);if (mid < crtr)modify_interval(i << 1|1, mid + 1, r, crtl, crtr, val);push_up(i);
}inline void modify_point(int i, int l, int r, int pos, int val){if (l == r){tag[i] = val;Max[i] = val;//tag会破坏掉addadd[i] = 0;return;}push_down(i);if (mid >= pos)modify_point(i << 1, l, mid, pos, val);elsemodify_point(i << 1|1, mid + 1, r, pos, val);push_up(i);
}

区间最大值查询

inline int query_max(int i, int l, int r, int crtl, int crtr){if (l > crtr || r < crtl)return 0;if (l >= crtl && r <= crtr){return Max[i];}int res = -INF;push_down(i);if (mid >= crtl)res = max(res, query_max(i << 1, l, mid, crtl, crtr));if (mid < crtr)res = max(res, query_max(i << 1|1, mid + 1, r, crtl, crtr));return res;
}

树上操作

代码大体上一样,值得注意的是当u和v在同一条重链上时通过swap使dep[u] <= dep[v],此时编号为dfn[u]节点的权值是它的父节点到它的权值,不应该算进来

inline void tree_add(int u, int v, int val){while (top[u] != top[v]){if (dep[top[u]] < dep[top[v]])swap(u, v);add_interval(1, 1, n, dfn[top[u]], dfn[u], val);u = par[top[u]];}if (dep[u] > dep[v])swap(u, v);//在同一条重链上,+1后必然还在这条链上add_interval(1, 1, n, dfn[u] + 1, dfn[v], val);
}inline void tree_modify(int u, int v, int val){while (top[u] != top[v]){if (dep[top[u]] < dep[top[v]])swap(u, v);modify_interval(1, 1, n, dfn[top[u]], dfn[u], val);u = par[top[u]];}if (dep[u] > dep[v])swap(u, v);modify_interval(1, 1, n, dfn[u] + 1, dfn[v], val);
}inline int tree_max(int u, int v){int res = -INF;while (top[u] != top[v]){if (dep[top[u]] < dep[top[v]])swap(u, v);res = max(res, query_max(1, 1, n, dfn[top[u]], dfn[u]));u = par[top[u]];}if (dep[u] > dep[v])swap(u, v);res = max(res, query_max(1, 1, n, dfn[u] + 1, dfn[v]));return res;
}

主函数

值得一提的是 Change k 是将第k条树枝边的边权改变为w, 我们要简单判断一下第k条边u <–> v的边权附给了那个节点。只需要通过dfs顺序dfn的大小或者是u,v的深度判断那个节点是子节点,再进行单点修改即可

int main() {//freopen("in.txt", "r", stdin);memset(tag, -1, sizeof(tag));n = read();for (register int i = 1, x, y, z; i <= n - 1; ++i) {x = read(), y = read(), z = read();add_edge(x, y, z), add_edge(y, x, z);}dfs1(1, 0);dfs2(1, 1);buildTree(1, 1, n);char opt[6] = {0};int a = 0, b = 0, c = 0;while (true){scanf("%s", opt);if (opt[0] == 'S')break;a = read(), b = read();if (opt[0] == 'M')printf("%d\n", tree_max(a, b));if (opt[1] == 'h'){a *= 2;int u = e[a].fr, v = e[a].to;int x = dfn[u] > dfn[v] ? u : v;modify_point(1, 1, n, dfn[x], b);}if (opt[1] == 'o'){c = read();tree_modify(a, b, c);}if (opt[1] == 'd'){c = read();tree_add(a, b, c);}}return 0;
}

完整代码

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cctype>
#define mid (l + r >> 1)
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int n, tot, cnt;
int head[N];
int dep[N], size[N],par[N], son[N], w[N];
int dfn[N], pre[N], top[N];
struct edge{int fr, to, nxt, val;
}e[N << 1];inline int read(){int x = 0, op = 1;char ch = getchar();while (!isdigit(ch)){if (ch == '-') op = -1;ch = getchar();}while (isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}return x * op;
}inline void add_edge(int u, int v, int w){e[++tot] = {u, v, head[u], w};head[u] = tot;
}void dfs1(int u, int fa){size[u] = 1;dep[u] = dep[fa] + 1;par[u] = fa;for (int i = head[u];i; i = e[i].nxt) {int v = e[i].to;if (v == fa) continue;w[v] = e[i].val;dfs1(v, u);size[u] += size[v];if (size[v] > size[son[u]])son[u] = v;}
}void dfs2(int u, int tp){top[u] = tp;dfn[u] = ++cnt;pre[cnt] = u;if (son[u])dfs2(son[u], tp);for (int i = head[u]; i; i = e[i].nxt) {int v = e[i].to;if (v != par[u] && v != son[u])dfs2(v, v);}
}int Max[N << 2], add[N << 2], tag[N << 2];inline void push_up(int i){Max[i] = max(Max[i << 1], Max[i << 1|1]);
}inline void push_down(int i){if (tag[i] != -1){add[i << 1] = add[i << 1|1] = 0;tag[i << 1] = tag[i <<1|1] = tag[i];Max[i << 1] = Max[i << 1|1] = tag[i];tag[i] = -1;}if (add[i] == 0) return;add[i << 1] += add[i];add[i << 1|1] += add[i];Max[i << 1] += add[i];Max[i << 1|1] += add[i];add[i] = 0;
}inline void buildTree(int i, int l, int r){if (l == r){Max[i] = w[pre[l]];return;}buildTree(i << 1, l, mid);buildTree(i << 1|1, mid + 1, r);push_up(i);
}inline void add_interval(int i, int l, int r, int crtl, int crtr, int val){if (l > crtr || r < crtl)return;if (l >= crtl && r <= crtr){add[i] += val;Max[i] += val;return;}push_down(i);if (mid >= crtl)add_interval(i << 1, l, mid, crtl, crtr, val);if (mid < crtr)add_interval(i << 1|1, mid + 1, r, crtl, crtr, val);push_up(i);
}inline void modify_interval(int i, int l, int r, int crtl, int crtr, int val){if (l > crtr || r < crtl)return;if (l >= crtl && r <= crtr){tag[i] = val;Max[i] = val;add[i] = 0;return;}push_down(i);if (mid >= crtl)modify_interval(i << 1, l, mid, crtl, crtr, val);if (mid < crtr)modify_interval(i << 1|1, mid + 1, r, crtl, crtr, val);push_up(i);
}inline void modify_point(int i, int l, int r, int pos, int val){if (l == r){tag[i] = val;Max[i] = val;add[i] = 0;return;}push_down(i);if (mid >= pos)modify_point(i << 1, l, mid, pos, val);elsemodify_point(i << 1|1, mid + 1, r, pos, val);push_up(i);
}inline int query_max(int i, int l, int r, int crtl, int crtr){if (l > crtr || r < crtl)return 0;if (l >= crtl && r <= crtr){return Max[i];}int res = -INF;push_down(i);if (mid >= crtl)res = max(res, query_max(i << 1, l, mid, crtl, crtr));if (mid < crtr)res = max(res, query_max(i << 1|1, mid + 1, r, crtl, crtr));return res;
}inline void tree_add(int u, int v, int val){while (top[u] != top[v]){if (dep[top[u]] < dep[top[v]])swap(u, v);add_interval(1, 1, n, dfn[top[u]], dfn[u], val);u = par[top[u]];}if (dep[u] > dep[v])swap(u, v);add_interval(1, 1, n, dfn[u] + 1, dfn[v], val);
}inline void tree_modify(int u, int v, int val){while (top[u] != top[v]){if (dep[top[u]] < dep[top[v]])swap(u, v);modify_interval(1, 1, n, dfn[top[u]], dfn[u], val);u = par[top[u]];}if (dep[u] > dep[v])swap(u, v);modify_interval(1, 1, n, dfn[u] + 1, dfn[v], val);
}inline int tree_max(int u, int v){int res = -INF;while (top[u] != top[v]){if (dep[top[u]] < dep[top[v]])swap(u, v);res = max(res, query_max(1, 1, n, dfn[top[u]], dfn[u]));u = par[top[u]];}if (dep[u] > dep[v])swap(u, v);res = max(res, query_max(1, 1, n, dfn[u] + 1, dfn[v]));return res;
}int main() {//freopen("in.txt", "r", stdin);memset(tag, -1, sizeof(tag));n = read();for (register int i = 1, x, y, z; i <= n - 1; ++i) {x = read(), y = read(), z = read();add_edge(x, y, z), add_edge(y, x, z);}dfs1(1, 0);dfs2(1, 1);buildTree(1, 1, n);char opt[6] = {0};int a = 0, b = 0, c = 0;while (true){scanf("%s", opt);if (opt[0] == 'S')break;a = read(), b = read();if (opt[0] == 'M')printf("%d\n", tree_max(a, b));if (opt[1] == 'h'){a *= 2;int u = e[a].fr, v = e[a].to;int x = dfn[u] > dfn[v] ? u : v;modify_point(1, 1, n, dfn[x], b);}if (opt[1] == 'o'){c = read();tree_modify(a, b, c);}if (opt[1] == 'd'){c = read();tree_add(a, b, c);}}return 0;
}

测试样例

/**
input:4
1 2 8
1 3 7
3 4 9
Max 2 4
Cover 2 4 5
Add 1 4 10
Change 1 16
Max 2 4
Stop
output:
9
16
*/

【洛谷P4315】月下毛景树 树链剖分03相关推荐

  1. 洛谷P4315 月下“毛景树” 题解

    洛谷P4315 月下"毛景树" 题解 题目链接:P4315 月下"毛景树" 题意:请维护一个数据结构,支持 改第 kkk 条边的边权 结点 uuu 到 vvv ...

  2. 洛谷P4315 月下“毛景树”

    题目描述 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里. 爬啊爬~爬啊爬毛毛虫爬到了一颗小小的"毛景树&quo ...

  3. 洛谷 P4315 月下“毛景树”(边树剖)

    题目不算难,但是代码量需要控制 主要说一下线段树上的操作,因为有两个相关的区间操作标记,应该先覆盖后增加,因为覆盖操作会影响增加操作 const int N=1e5+5;int n,m;int i,j ...

  4. P4315 月下“毛景树” (树链剖分)

    题目链接: P4315 月下"毛景树" 大致题意 给定一棵由nnn个节点的树, 由n−1n - 1n−1带权边构成. 有如下444种操作: Change k c: 把第kkk条边的 ...

  5. 树剖+线段树||树链剖分||BZOJ1984||Luogu4315||月下“毛景树”

    题面:月下"毛景树" 题解:是道很裸的树剖,但处理的细节有点多(其实是自己线段树没学好).用一个Dfs把边权下移到点权,用E数组记录哪些边被用到了:前三个更新的操作都可以合并起来, ...

  6. [bzoj1984]月下“毛景树” 树链剖分

    1984: 月下"毛景树" Time Limit: 20 Sec  Memory Limit: 64 MB [Submit][Status][Discuss] Descriptio ...

  7. BZOJ1984: 月下“毛景树”

    BZOJ 1984: 月下"毛景树" Time Limit: 20 Sec  Memory Limit: 64 MB Submit: 1583  Solved: 500 [Subm ...

  8. 洛谷 P3373 【模板】线段树 2 题解

    洛谷 P3373 [模板]线段树 2 题解 题面 题目链接:[戳这里](https://www.luogu.org/problemnew/show/P3373) 题目描述 输入输出格式 输入输出样例 ...

  9. 信息学奥赛一本通 1365:FBI树(fbi) | 1928:【04NOIP普及组】FBI树 | 洛谷 P1087 [NOIP2004 普及组] FBI 树

    [题目链接] ybt 1365:FBI树(fbi) ybt 1928:[04NOIP普及组]FBI树 洛谷 P1087 [NOIP2004 普及组] FBI 树 [题目考点] 1. 二叉树 [解题思路 ...

  10. [BZOJ1984] 月下“毛景树”

    Description 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里.爬啊爬~爬啊爬~~毛毛虫爬到了一颗小小的" ...

最新文章

  1. php 二维数组排序,多维数组排序
  2. 分库分表:如何做到永不迁移数据和避免热点?
  3. 星巴克CEO一年赚的还没TikTok网红多???
  4. DataGridView显示数据库数据(一)
  5. mysql sequence 关键字_mysql增加sequence功能
  6. Java黑皮书课后题第7章:***7.35(游戏:猜字词游戏)编写一个猜字词游戏,随机产生一个单词,当用户猜测正确后,正确的字母显示出来。当用户猜出一个单词,显示猜错的次数,并询问用户是否继续猜测
  7. Equipment download - No data contained in BDoc
  8. 4152. [AMPPZ2014]The Captain(稠密图最短路)
  9. css下拉点击不动,CSS3 - 进行简单的点击下拉[关闭](CSS3 - Making a simple click-dropdown [closed])...
  10. js进阶 10-1 JQuery是什么
  11. 最新众信支付php开源,pay: ThinkPHP开源聚合支付系统
  12. Windows下载执行命令大全
  13. b类 蚂蚁金服_蚂蚁金服开放平台 - 文档中心
  14. Wed Dec 20 2019 00:00:00 GMT+0800 (中国标准时间) 时间转换
  15. 利用PS如何进行精细抠图
  16. 第八届ACM趣味编程循环赛重现赛(部分题目)
  17. 虫儿飞简谱用计算机,乐曲简谱(虫儿飞简谱)
  18. wps 单元格跳动_WPS文字在表格中打字自动跳动
  19. 如何使用UCI数据集
  20. Tomcat的访问及修改端口号

热门文章

  1. QuickJS 数字字面量解析
  2. 读书笔记--《原则》
  3. python笔记11 - lambda函数,globals()/locals()函数,eval()exec()函数,闭包函数,函数式编程,高阶函数
  4. 写的一款安全期避孕计算软件 (IWOMAN女性生理周期计算) 强力推荐!
  5. linux 命令:du 详解
  6. 笔记本升级--老华硕的升级之路
  7. maven docker 部署到多台机器上。。_TensorFlow Serving + Docker + Tornado机器学习模型生产级快速部署
  8. OpenGauss/MogDB调用C FUNCTION 范例
  9. #树形dp#洛谷 2014 codevs 1378 jzoj 1486 选课
  10. 广东工业大学计算机专业分流,2017年广东工业大学大类招生学生专业分流工作小组...