bzoj4127 Abs 树链剖分+线段树+均摊分析
题目传送门
https://lydsy.com/JudgeOnline/problem.php?id=4127
题解
首先区间绝对值和可以转化为 \(2\) 倍的区间正数和 \(-\) 区间和。于是问题就转化为区间正数和。
因为每一次增加的量 \(d \geq 0\),所以每一个数只会被从负数变成正数一次。也就是说,从负变正的操作最多出现 \(n\) 次。
于是我们考虑在线段树上对于从负数变成正数的操作暴力修改。
如何判断一个区间内有负数变成正数的操作呢。令 \(c\) 表示这个区间的最大负数。于是如果 \(-k \leq c \leq 0\) 那么说明区间有数从负变正。
那么每一次从负变正都会走 \(\log n\) 个线段树节点,于是所有数从负变正而来的总时间为 \(O(n\log n)\)。
其余的修改的复杂度为 \(O(q\log n)\)。
时间复杂度为 \(O((n+q)\log n)\)。
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
#define lc o << 1
#define rc o << 1 | 1
const int N = 1e5 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n, m, dfc;
int a[N];
int dep[N], f[N], siz[N], son[N], dfn[N], pre[N], top[N];
struct Edge { int to, ne; } g[N << 1]; int head[N], tot;
inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y) { addedge(x, y), addedge(y, x); }
struct Node { ll sum, add, sumc, maxf; int s; } t[N << 2];
inline void pushup(int o, int L, int R) {
t[o].s = t[lc].s + t[rc].s;
t[o].sum = t[lc].sum + t[rc].sum + t[o].add * (R - L + 1);
t[o].sumc = t[lc].sumc + t[rc].sumc + t[o].add * t[o].s;
t[o].maxf = -INF;
if (t[lc].maxf) smax(t[o].maxf, t[lc].maxf);
if (t[rc].maxf) smax(t[o].maxf, t[rc].maxf);
if (t[o].maxf == -INF) t[o].maxf = 0;
t[o].maxf && (t[o].maxf += t[o].add);
// dbg("o = %d, L = %d, R = %d, t[o].maxf = %lld, t[o].s = %d\n", o, L, R, t[o].maxf, t[o].s);
assert(t[o].maxf || (t[o].maxf == 0 && t[o].s == R - L + 1));
}
inline void pushdown(int o, int L, int R) {
if (!t[o].add) return;
int M = (L + R) >> 1;
t[lc].sum += t[o].add * (M - L + 1), t[lc].add += t[o].add;
t[lc].sumc += t[o].add * t[lc].s, t[lc].maxf && (t[lc].maxf += t[o].add);
t[rc].sum += t[o].add * (R - M), t[rc].add += t[o].add;
t[rc].sumc += t[o].add * t[rc].s, t[rc].maxf && (t[rc].maxf += t[o].add);
t[o].add = 0;
}
inline void build(int o, int L, int R) {
t[o].add = 0;
if (L == R) {
t[o].sum = a[pre[L]];
t[o].maxf = std::min(0, a[pre[L]]);
t[o].sumc = std::max(a[pre[L]], 0);
t[o].s = a[pre[L]] >= 0;
return;
}
int M = (L + R) >> 1;
build(lc, L, M), build(rc, M + 1, R);
pushup(o, L, R);
}
inline void qadd(int o, int L, int R, int l, int r, int k) {
// dbg("qadd : o = %d, L = %d, R = %d, l = %d, r = %d, k = %d, t[o].maxf = %lld, t[o].sum = %lld, t[o].sumc = %lld, t[o].s = %d, t[o].add = %lld\n", o, L, R, l, r, k, t[o].maxf, t[o].sum, t[o].sumc, t[o].s, t[o].add);
if (l <= L && R <= r && (!t[o].maxf || t[o].maxf < -k)) {
t[o].sum += k * (ll)(R - L + 1);
t[o].sumc += k * (ll)t[o].s;
t[o].add += k;
if (t[o].maxf) t[o].maxf += k;
return;
}
if (L == R) {
t[o].sum += k, t[o].add += k;
t[o].sumc = std::max(0ll, t[o].sum);
t[o].s = t[o].sum >= 0;
t[o].maxf = std::min(0ll, t[o].sum);
return;
}
int M = (L + R) >> 1;
pushdown(o, L, R);
if (l <= M) qadd(lc, L, M, l, r, k);
if (r > M) qadd(rc, M + 1, R, l, r, k);
pushup(o, L, R);
}
inline ll qsum(int o, int L, int R, int l, int r) {
// dbg("qsum : o = %d, L = %d, R = %d, l = %d, r = %d\n", o, L, R, l, r);
if (l <= L && R <= r) return t[o].sumc * 2 - t[o].sum;
int M = (L + R) >> 1;
pushdown(o, L, R);
if (r <= M) return qsum(lc, L, M, l, r);
if (l > M) return qsum(rc, M + 1, R, l, r);
return qsum(lc, L, M, l, r) + qsum(rc, M + 1, R, l, r);
}
inline void upd(int x, int y, int k) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
qadd(1, 1, n, dfn[top[x]], dfn[x], k);
x = f[top[x]];
}
if (dep[x] > dep[y]) std::swap(x, y);
qadd(1, 1, n, dfn[x], dfn[y], k);
}
inline ll qry(int x, int y) {
ll ans = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
ans += qsum(1, 1, n, dfn[top[x]], dfn[x]);
x = f[top[x]];
}
if (dep[x] > dep[y]) std::swap(x, y);
return ans += qsum(1, 1, n, dfn[x], dfn[y]);
}
inline void dfs1(int x, int fa = 0) {
dep[x] = dep[fa] + 1, f[x] = fa, siz[x] = 1;
for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);
}
inline void dfs2(int x, int pa) {
// dbg("x = %d, pa = %d\n", x, pa);
top[x] = pa, dfn[x] = ++dfc, pre[dfc] = x;
if (!son[x]) return; dfs2(son[x], pa);
for fec(i, x, y) if (y != son[x] && y != f[x]) dfs2(y, y);
}
inline void work() {
dfs1(1), dfs2(1, 1), build(1, 1, n);
while (m--) {
int opt, x, y, z;
read(opt), read(x), read(y);
if (opt == 1) read(z), upd(x, y, z);
else printf("%lld\n", qry(x, y));
}
}
inline void init() {
read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i]);
int x, y;
for (int i = 1; i < n; ++i) read(x), read(y), adde(x, y);
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
bzoj4127 Abs 树链剖分+线段树+均摊分析的更多相关文章
- 【bzoj4127】Abs 树链剖分+线段树
题目描述 给定一棵树,设计数据结构支持以下操作 1 u v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 输入 第一行两个整数n和m,表示结点个数和操作数 ...
- BZOJ4127Abs——树链剖分+线段树
题目描述 给定一棵树,设计数据结构支持以下操作 1 u v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 输入 第一行两个整数n和m,表示结点个数和操作数 ...
- 【BZOJ-2325】道馆之战 树链剖分 + 线段树
2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 1153 Solved: 421[Submit][Statu ...
- HDU4897 (树链剖分+线段树)
Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...
- BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树
题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...
- BZOJ2325[ZJOI2011]道馆之战——树链剖分+线段树
题目描述 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中 的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才 ...
- 【bzoj3083】遥远的国度 树链剖分+线段树
题目描述 描述zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn ...
- 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树
[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...
- BZOJ2243 (树链剖分+线段树)
Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...
- POJ3237 (树链剖分+线段树)
Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...
随机推荐
- NAT网关之SNAT进阶使用(一)SNAT POOL
摘要: NAT网关是云上VPC ECS访问Internet的出入口.SNAT可实现指定的VPC ECS使用指定的公网IP访问互联网.阿里云NAT网关控制台创建SNAT条目时,默认是为指定的交换机配置1 ...
- Python编程:从入门到实践—类
创建类 #!/usr/bin/env python# --*-- encoding:utf-8 --*-- class Dog(): """一次模拟小狗的简单尝试&quo ...
- 有关OI的思维导图
看到这张图 我仿佛掉进了知识的深渊QwQ
- 小程序mpvue中动态切换echarts图表
如果你用mpvue,而且还想用echarts,那么这个包你可以以来一下 https://github.com/F-loat/mpvue-echarts 考虑到多个页面都休要用,所以抽出来作为一个组件, ...
- 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_08 转换流_4_OutputStreamWriter介绍&代码实现
父类是Write 需要抛出异常 声明为它的父类IO异常也是可以的 不指定 默认就是utf-8格式的 两个汉字 6个字节 GBK格式 打开文件的位置用文本打开
- lateral view 使用方法
这个函数相当于拆开行变成列. 可以理解为行转列. select id,order_label from table_bx lateral view explode(split(work_order_l ...
- Super Mario HDU 4417 主席树区间查询
Super Mario HDU 4417 主席树区间查询 题意 给你n个数(编号从0开始),然后查询区间内小于k的数的个数. 解题思路 这个可以使用主席树来处理,因为这个很类似查询区间内的第k小的问题 ...
- UVA 10003 Cutting Sticks 区间DP+记忆化搜索
UVA 10003 Cutting Sticks+区间DP 纵有疾风起 题目大意 有一个长为L的木棍,木棍中间有n个切点.每次切割的费用为当前木棍的长度.求切割木棍的最小费用 输入输出 第一行是木棍的 ...
- vue 跳转页面返回时tab状态有误的解决办法
一.前言 最近在做新vue项目的时候遇到了一个问题,就是tab间的切换没有问题,当跳转到其他页面时,且这个页面并非子路由,再用浏览器的返回按钮返回首页时,tab的active始终指向默认的第一个选项. ...
- Tornado 的核是什么??
Tornado 的核心是 ioloop 和 iostream 这两个模块,前者提供了一个高效的 I/O 事件循环,后 者则封装了 一个无阻塞的 socket .通过向 ioloop 中添加网络 I/O ...