题目链接

题目

题目描述

\(Keven\) 特别喜欢线段树,他给你一个长度为 \(n\) 的序列,对序列进行 \(m\) 次操作。

操作有两种:

1 \(1\ l\ r\ k\) :表示将下标在 \([l , r]\) 区间内的数字替换成 \([k,k+1,…,k+r-l]\)

\(2\ l\ r\) :表示查询区间 \([l , r]\) 的区间和

输入描述

第一行两个整数 \(n、m\) ,表示序列的长度和操作次数 \((1 \leq n,m \leq 2 \times 10^5)\) 第二行 \(n\) 个整数,表示序列的初始值 \(a_1,a_2,…a_n(1 \leq a_i \leq 2 \times 10^5)\) 接下来 \(m\) 行,每行三或四个数字,若第一个数字是 \(1\) ,则表示操作 \(1\) ,反之则表示操作 \(2\) 。\((1 \leq l \leq r \leq n,1 \leq k \leq 2e5)\)

输出描述

对于每个操作 \(2\) ,输出一行一个整数表示区间和。

示例1

输入

5 5
1 1 1 1 1
2 1 5
1 1 5 1
2 1 5
1 1 3 3
2 1 3

输出

5
15
12

说明

第一次1操作后,序列是1 2 3 4 5

第二次1操作后,序列是3 4 5 4 5

题解

知识点:线段树,数学。

本题的区间修改不具有区间无关性,因为在一次修改的不同的子区间,修改维护的值是不同的,因此需要在 update 函数内做特殊处理,是非标准的线段树。

区间信息需要维护区间长度 \(len\) 、区间和 \(sum\) ,区间合并直接加即可。

区间修改需要维护数值起始点 \(k\) ,新区间和可以直接公式求出 \(\dfrac{(2k + len - 1)len}{2}\) 。

区间修改可以设置懒标记,只需要记录最新一次的修改即可。

注意,要考虑标记的单位元值,即表示未修改过的标记值,这里取 \(-1\) 。在 push_down 函数中需先判断标记是否为 \(-1\) ,再进行下传。

区间更新函数有两种写法:

  1. 不更改修改值,在更新时根据子区间位置直接求出实际修改值。
void update(int rt, int l, int r, int x, int y, F f) {
if (r < x || y < l) return;
if (x <= l && r <= y) return node[rt] = F{ f.k + l - x }(node[rt]), lazy[rt] = F{ f.k + l - x }(lazy[rt]), void();
push_down(rt);
int mid = l + r >> 1;
update(rt << 1, l, mid, x, y, f);
update(rt << 1 | 1, mid + 1, r, x, y, f);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
}
  1. 每次更新子区间时,同时更改相应的修改值。注意,这种方法需要控制更新的子区间,否则无法计算相应的修改值。
void update(int rt, int l, int r, int x, int y, F f) {
if (x == l && r == y) return node[rt] = f(node[rt]), lazy[rt] = f(lazy[rt]), void();
push_down(rt);
int mid = l + r >> 1;
if (y <= mid) update(rt << 1, l, mid, x, y, f);
else if (x >= mid + 1) update(rt << 1 | 1, mid + 1, r, x, y, f);
else update(rt << 1, l, mid, x, mid, f), update(rt << 1 | 1, mid + 1, r, mid + 1, y, { f.k + mid + 1 - x });
node[rt] = node[rt << 1] + node[rt << 1 | 1];
}

实际上,后者精确控制更新区间的方法,在遇到其他因为修改与区间位置有关等问题,需要魔改 update 函数时,更好用。

时间复杂度 \(O((n+m)\log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; struct T {
int len;
ll sum;
static T e() { return { 0,0 }; }
friend T operator+(const T &a, const T &b) { return { a.len + b.len,a.sum + b.sum }; }
};
struct F {
int k;
static F e() { return{ -1 }; }
T operator()(const T &x) { return { x.len, (2LL * k + x.len - 1) * x.len / 2 }; }
F operator()(const F &g) { return { k }; }
};
class SegmentTreeLazy {
int n;
vector<T> node;
vector<F> lazy; void push_down(int rt) {
if (!~lazy[rt].k) return;
node[rt << 1] = lazy[rt](node[rt << 1]);
lazy[rt << 1] = lazy[rt](lazy[rt << 1]);
lazy[rt].k += node[rt << 1].len;
node[rt << 1 | 1] = lazy[rt](node[rt << 1 | 1]);
lazy[rt << 1 | 1] = lazy[rt](lazy[rt << 1 | 1]);
lazy[rt] = F::e();
} /* void update(int rt, int l, int r, int x, int y, F f) {
if (r < x || y < l) return;
if (x <= l && r <= y) return node[rt] = F{ f.k + l - x }(node[rt]), lazy[rt] = F{ f.k + l - x }(lazy[rt]), void();
push_down(rt);
int mid = l + r >> 1;
update(rt << 1, l, mid, x, y, f);
update(rt << 1 | 1, mid + 1, r, x, y, f);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
} */ void update(int rt, int l, int r, int x, int y, F f) {
if (x == l && r == y) return node[rt] = f(node[rt]), lazy[rt] = f(lazy[rt]), void();
push_down(rt);
int mid = l + r >> 1;
if (y <= mid) update(rt << 1, l, mid, x, y, f);
else if (x >= mid + 1) update(rt << 1 | 1, mid + 1, r, x, y, f);
else update(rt << 1, l, mid, x, mid, f), update(rt << 1 | 1, mid + 1, r, mid + 1, y, { f.k + mid + 1 - x });
node[rt] = node[rt << 1] + node[rt << 1 | 1];
} T query(int rt, int l, int r, int x, int y) {
if (r < x || y < l) return T::e();
if (x <= l && r <= y) return node[rt];
push_down(rt);
int mid = l + r >> 1;
return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y);
}
public:
SegmentTreeLazy(int _n = 0) { init(_n); }
SegmentTreeLazy(const vector<T> &src) { init(src); } void init(int _n) {
n = _n;
node.assign(n << 2, T::e());
lazy.assign(n << 2, F::e());
}
void init(const vector<T> &src) {
assert(src.size());
init(src.size() - 1);
function<void(int, int, int)> build = [&](int rt, int l, int r) {
if (l == r) return node[rt] = src[l], void();
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
};
build(1, 1, n);
} void update(int x, int y, F f) { update(1, 1, n, x, y, f); } T query(int x, int y) { return query(1, 1, n, x, y); }
}; int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector<T> a(n + 1);
for (int i = 1;i <= n;i++) {
int x;
cin >> x;
a[i] = { 1,x };
}
SegmentTreeLazy sgt(a);
while (m--) {
int op, l, r;
cin >> op >> l >> r;
if (op == 1) {
int k;
cin >> k;
sgt.update(l, r, { k });
}
else cout << sgt.query(l, r).sum << '\n';
}
return 0;
}

NC200195 区区区间的更多相关文章

  1. 牛客网 223C 区区区间间间(单调栈)

    题目链接:区区区间间间 题意:给出长度为n的数字序列ai,定义区间(l,r)的价值为, 请你计算出. 题解:单调栈求ai左边和右边第一个比它小的位置,需要减去ai的个数为$(R_i-i+1)*(i-L ...

  2. NowCoder--牛可乐发红包脱单ACM赛C_区区区间间间

    题目链接:C_区区区间间间 思路:算贡献,求出每个数为当前最大值时所在的区间个数,和每个数为最小值的区间个数 和这个题有点类似 搭配食用效果更佳 点击这里 #include<bits/stdc+ ...

  3. 牛客ACM赛 C 区区区间间间

    链接 C 区区区间间间 给定长度为\(n\)序列,求\[\sum_{i=1}^{n} \sum_{j=i}^{n} max-min\] 其中\(max\),\(min\)为区间最大,最小值,\(n\l ...

  4. NC20806 区区区间间间

    NC20806 区区区间间间 题目 题目描述 给出长度为n的序列a,其中第i个元素为 \(a_i\),定义区间(l,r)的价值为 \(v_{l,r} = max(a_i - a_j | l \leqs ...

  5. 【ACM算法竞赛日常训练】DAY16【奇♂妙拆分】【区区区间间间】【小AA的数列】数学 | 位运算 | 前缀和

    DAY16共3题: 奇♂妙拆分(简单数学) 区区区间间间(单调栈) 小AA的数列(位运算dp) 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的方式讲解算法!️ ...

  6. 牛客网 牛可乐发红包脱单ACM赛 C题 区区区间间间

    [题解] 我想到了两种解法. 一种是扫描线+线段树+单调栈. 另一种方法是O(n)的,比较巧妙. 考虑每个数在哪些区间可以作为最小数.最长的区间就是它向左右走,直到有数字比它小,这个可以用单调栈维护. ...

  7. [nowcoderACM_223C][区区区间间间]

    题目链接 思路 考虑用单调栈,栈顶为最大元素.当得到一个新值得时候,将这个值宇栈顶比较.因为栈顶是前面的最大元素.所以只要当前元素比栈顶大,那么肯定比前面的都大.只要将这个元素乘上前面的个数就行了. ...

  8. ASP.NET Core应用针对静态文件请求的处理[2]: 条件请求与区间请求

    通过调用ApplicationBuilder的扩展方法UseStaticFiles注册的StaticFileMiddleware中间件帮助我们处理针对文件的请求.对于StaticFileMiddlew ...

  9. SQL Server 随机数,随机区间,随机抽取数据rand(),floor(),ceiling(),round(),newid()函数等

    在查询分析器中执行:select rand(),可以看到结果会是类似于这样的随机小数:0.36361513486289558,像这样的小数在实际应用中用得不多,一般要取随机数都会取随机整数.那就看下面 ...

  10. codevs 1082 线段树练习 3(区间维护)

    codevs 1082 线段树练习 3  时间限制: 3 s  空间限制: 128000 KB  题目等级 : 大师 Master 题目描述 Description 给你N个数,有两种操作: 1:给区 ...

随机推荐

  1. [转帖]十九、Linux性能优化实战学习笔记- 为什么系统的Swap变高了?

    目录 一.什么是文件页?什么是脏页?什么是匿名页? 二.linux swap原理 换出 换入 三.内存回收的时机 1.直接内存回收 2.kswapd0内核线程 四.NUMA 与 Swap关系 五.sw ...

  2. [转帖]Oracle性能优化-大内存页配置

    一.为什么需要大页面? 如果您有一个大的RAM和SGA,那么HugePages对于Linux上更快的Oracle数据库性能是至关重要的.如果您的组合数据库SGAs很大(比如超过8GB,甚至对于更小的数 ...

  3. [转帖]记一次sst文件损坏修复过程

    https://tidb.net/blog/54e388c8 [2023-07-14 14:26:28]应用系统报警删除数据失败,查看日志报Region is unavailable,同时企业微信群也 ...

  4. [转帖]Docker资源(CPU/内存/磁盘IO/GPU)限制与分配指南

    https://zhuanlan.zhihu.com/p/417472115 什么是cgroup? cgroups其名称源自控制组群(control groups)的简写,是Linux内核的一个功能, ...

  5. [转帖]Nginx中if语句中的判断条件

    https://www.cnblogs.com/songxingzhu/p/6382007.html 一.if语句中的判断条件(nginx) 1.正则表达式匹配: ==:等值比较; ~:与指定正则表达 ...

  6. 【转帖】ChatGPT的前身:InstructGPT

    https://www.jianshu.com/p/6daf35cbc46a ChatGPT的论文目前还没有发布,在其官方博客(https://openai.com/blog/chatgpt/)中对方 ...

  7. 【转帖】奇淫技巧 | route命令设置网络优先级

    奇淫技巧 | route命令设置网络优先级 https://blog.csdn.net/DynmicResource/article/details/120134745 1. 背景 在生活中的会经常遇 ...

  8. ARM平台安装Docker的方法

    1. 找了一下有一个网站能够下载docker的arm的deb包可以使用 网址为: https://download.docker.com/linux/ubuntu/dists/xenial/pool/ ...

  9. Linux的free命令与OOM的简单总结

    简介 查看操作系统配置最关键的几个命令是 lscpu 查看CPU信息 以及free 查看内存信息. 不过free信息有一个疑惑点 他的 free的值可能很小. 会让搭建产生误解. 这里简单说明一下. ...

  10. Oracle 查看所有表大小的SQL

    Oracle 查看所有表大小的SQL 比较坑的是 lob 字段和 表的大小不在一个地方 为了出结果 我这边使用了 union all 慢的一逼... SELECT sum( tablesize ), ...