题意:有一个数组a和一个数组k,数组a一直保持一个性质:a[i + 1] >= a[i] + k[i]。有两种操作:1,给某个元素加上x,但是加上之后要保持数组a的性质。比如a[i]加上x之后,a[i + 1]<a[i] + k[i],那么a[i + 1]就变成a[i] + k[i],否则不变。同理,若a[i + 2]小于了现在的a[i + 1] + k[i + 1],那么a[i + 2]也变成a[i + 1] + k[i + 1],一直保持这个性质。第二章操作,询问数组a的区间[l, r]的区间和。

思路:容易发现,假设更改了x位置之后,恰好到位置y不需要更改元素,那么及其之后的位置肯定就不用更改了。所以,在查找这个位置的时候,我们可以二分查找。找到之后,对区间[x, y]进行操作。我们可以发现,区间[x, y]的数有规律。假设x位置的数是a[x],那么a[x + 1]是a[x] + k[x], a[x +  2]是a[x] + k[x + 1] + k[x + 2],以此类推。这个区间的和可以分为2部分:[y - x + 1]个a[x],和关于k的部分。可以发现,k[x]出现了(r - l + 1)次,k[x + 1]出现了(r - l)次,以此类推。那么怎么O(1)获得这个东西呢?我们先预处理k数组的前缀和(假设前缀和数组是b),那么我们再求b的前缀和(假设是数组c),那么c[i]就是出现了i次k[1],i - 1次k[2],以此类推。那么区间[x, y]中关于k的和就可以得出了:c[y] - c[x - 1] - [y - x + 1] * b[x]。前面c[y] - c[x - 1]可以O(1)算出,而后面的部分比较麻烦。怎么维护后面[y - x + 1] * b[x]这个部分呢?我们发现a[x]正好出现[y - x + 1]次,这样我们可以把a[x] - b[x]用一个懒标记维护,下放标记的时候区间的和为c[y] - c[x - 1] + (r - l + 1) * val (val是a[x] - b[x])。

代码:

#include <bits/stdc++.h>
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define LL long long
using namespace std;
const int maxn = 100010;
const LL INF = 1e18;
LL a[maxn], b[maxn], c[maxn];
int n, m, now;
struct SegementTree{
LL sum, tot;
int l, r;
};
SegementTree tr[maxn * 4]; LL cal(int l, int r) {
return (c[r] - c[l]) - (r - l) * (b[l]);
}
void pushup(int x) {
tr[x].sum = tr[ls(x)].sum + tr[rs(x)].sum;
} void maintain(int x,LL tot) {
if(tot == -INF) return;
int l = tr[x].l, r = tr[x].r;
tr[x].sum = (r - l + 1) * tot + (c[r] - c[l - 1]);
tr[x].tot = tot;
} void pushdown(int x) {
maintain(ls(x), tr[x].tot);
maintain(rs(x), tr[x].tot);
tr[x].tot = -INF;
} void build(int x, int l, int r) {
tr[x].l = l, tr[x].r = r;
tr[x].tot = -INF;
if(l == r) {
tr[x].sum = a[l];
return;
}
int mid = (l + r) >> 1;
build(ls(x), l, mid);
build(rs(x), mid + 1, r);
pushup(x);
} LL query(int x, int l, int r, int ql, int qr) {
if(l >= ql && r <= qr) {
return tr[x].sum;
}
int mid = (l + r) >> 1;
LL ans = 0;
pushdown(x);
if(ql <= mid) ans += query(ls(x), l, mid, ql, qr);
if(qr > mid) ans += query(rs(x), mid + 1, r, ql, qr);
return ans;
} void update(int x, int l, int r, int ql, int qr, LL val) {
if(l >= ql && r <= qr) {
maintain(x, val);
return;
}
int mid = (l + r) >> 1;
pushdown(x);
if(mid >= ql) update(ls(x), l, mid, ql, qr, val);
if(mid < qr) update(rs(x), mid + 1, r, ql, qr, val);
pushup(x);
} int main() {
int x, y;
char s[5];
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
for (int i = 2; i <= n; i++) {
scanf("%lld", &b[i]);
b[i] += b[i - 1];
}
for (int i = 2; i <= n; i++)
c[i] = c[i - 1] + b[i];
build(1, 1, n);
scanf("%d", &m);
while(m--) {
scanf("%s%d%d", s + 1, &x, &y);
if(s[1] == '+') {
int l = x, r = n;
LL tmp = query(1, 1, n, x, x);
while(l < r) {
int mid = (l + r + 1) >> 1;
LL tmp1 = query(1 , 1, n, mid, mid);
if(tmp + y + b[mid] - b[x] > tmp1)
l = mid;
else r = mid - 1;
}
now = x;
update(1, 1, n, x, l, tmp + y - b[x]);
} else {
printf("%lld\n", query(1, 1, n, x, y));
}
}
}

  

Codeforces 1136E Nastya Hasn't Written a Legend (线段树教做人系列)的更多相关文章

  1. Codeforces 1136E - Nastya Hasn't Written a Legend - [线段树+二分]

    题目链接:https://codeforces.com/problemset/problem/1136/E 题意: 给出一个 $a[1 \sim n]$,以及一个 $k[1 \sim (n-1)]$, ...

  2. Codeforces 1136E Nastya Hasn't Written a Legend 线段树

    vp的时候没码出来.. 我们用set去维护, 每一块区域, 每块区域内的元素与下一个元素的差值刚好为ki,每次加值的时候我们暴力合并, 可以发现我们最多合并O(n)次. 然后写个线段树就没了. #in ...

  3. codeforces#1136E. Nastya Hasn't Written a Legend(二分+线段树)

    题目链接: http://codeforces.com/contest/1136/problem/E 题意: 初始有a数组和k数组 有两种操作,一,求l到r的区间和,二,$a_i\pm x$ 并且会有 ...

  4. Codeforces 719E (线段树教做人系列) 线段树维护矩阵

    题面简洁明了,一看就懂 做了这个题之后,才知道怎么用线段树维护递推式.递推式的递推过程可以看作两个矩阵相乘,假设矩阵A是初始值矩阵,矩阵B是变换矩阵,求第n项相当于把矩阵B乘了n - 1次. 那么我们 ...

  5. [Codeforces 464E] The Classic Problem(可持久化线段树)

    [Codeforces 464E] The Classic Problem(可持久化线段树) 题面 给出一个带权无向图,每条边的边权是\(2^{x_i}(x_i<10^5)\),求s到t的最短路 ...

  6. Codeforces Round #244 (Div. 2) B. Prison Transfer 线段树rmq

    B. Prison Transfer Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/problemset/pro ...

  7. Codeforces Round #603 (Div. 2) E. Editor(线段树)

    链接: https://codeforces.com/contest/1263/problem/E 题意: The development of a text editor is a hard pro ...

  8. Educational Codeforces Round 6 E. New Year Tree dfs+线段树

    题目链接:http://codeforces.com/contest/620/problem/E E. New Year Tree time limit per test 3 seconds memo ...

  9. codeforces 893F - Physical Education Lessons 动态开点线段树合并

    https://codeforces.com/contest/893/problem/F 题意: 给一个有根树, 多次查询,每次查询对于$x$i点的子树中,距离$x$小于等于$k$的所有点中权值最小的 ...

随机推荐

  1. 学习笔记之Microsoft Azure

    Microsoft Azure - Wikipedia https://en.wikipedia.org/wiki/Microsoft_Azure Microsoft Azure (formerly  ...

  2. autopy的使用探索

    autopy这个包可以直接操作电脑的一些行为,比如点击,移动鼠标啥的,但是可能只使用于windows,用处有限,不过做个简单的游戏辅助还是可以的. 网上资料贼少,很多不能用的,现总结一些可以用的放在这 ...

  3. Python单例模式的4种实现方法

    #-*- encoding=utf-8 -*- print '----------------------方法1--------------------------' #方法1,实现__new__方法 ...

  4. tornado.gen 模块解析

    转自:http://strawhatfy.github.io/2015/07/22/Tornado.gen/ 引言 注:正文中引用的 Tornado 代码除特别说明外,都默认引用自 Tornado 4 ...

  5. 在docker中运行jenkins实现代码自动发布到测试服务器

    在docker中运行jenkins 用的镜像是apline版:lts-alpine,并设置正确的时区. docker run --name jenkins_master -d \ -p 8081:80 ...

  6. 洛谷:P1087 FBI树 P1030 求先序排列 P1305 新二叉树

    至于为啥把这三个题放到一起,大概是因为洛谷的试炼场吧,三道树的水题,首先要理解 先序中序后序遍历方法. fbi树由于数量小,在递归每个区间时,暴力跑一遍区间里的数,看看是否有0和1.至于递归的方法,二 ...

  7. webpack(2)--Entry

    Entry entry是配置模块的入口,可以抽象成输入,webpack执行构建的第一步将从入口开始搜寻及递归解析出所有入口依赖的模块. 注意: entry是必填,若不填写则将导致webpack报错退出 ...

  8. SPM——Using Maven+Junit to test Hello Wudi

    Last week, ours teacher taught us 'Software Delivery and Build Management'. And in this class, our t ...

  9. VueCli

    Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,Vue CLI 致力于将 Vue 生态中的工具基础标准化.它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写 ...

  10. HTML5进阶

    内容: 1.geolocation元素 2.video元素和audio元素 3.localStorage 4.WebWorker 5.WebSQL.IndexedDB 6.文件操作.文件拖拽新概念 7 ...