题目描述
题解
不知道如何想到差分…反正正解很玄学…
如果对于i来说需要满足赋值条件,满足条件 a i + 1 − a i < k i a_{i+1}-a_i<k_i ai+1−ai<ki。
如果令 c i = ∑ j = 1 i k i c_i=\sum_{j=1}^{i} k_i ci=∑j=1iki,就有 a i + 1 − a i < c i − c i − 1 a_{i+1}-a_i<c_i-c_{i-1} ai+1−ai<ci−ci−1,然后就有 a i + 1 − c i < a i − c i − 1 a_{i+1}-c{i}<a_i-c_{i-1} ai+1−ci<ai−ci−1
再另 b i = a i − c i − 1 b_i=a_i-c_{i-1} bi=ai−ci−1,则有 b i + 1 < b i b_{i+1}<b{i} bi+1<bi.因此我们只需要用维护单调不下降的b就行。
具体如何实现呢?
我们需要快速找到连续的一段区间并进行修改,可以使用线段数来维护;并且找到第一个符合条件的数字,将这个点左边的所有点修改即可;即找到第一个大于或等于 b i b_i bi的数,然后全部修改成 b i b_i bi即可。
至于线段数如何找到最大值,维护区间最大值即可。利用分治的思想就可以做到。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200000;
int n, Q;
int a[N], k[N], c[N], S[N], b[N];
struct node {
int max, sum, tag, l, r;
} t[N*10];
void build(int p,int l,int r)
{
t[p].l = l, t[p].r = r, t[p].tag = -233;
if (l == r) {
t[p].max = t[p].sum = b[l];
return;
}
int mid = l+r >> 1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].max = max(t[p*2].max,t[p*2+1].max);
t[p].sum = t[p*2].sum+t[p*2+1].sum;
return;
}
void spread(int p)
{
if (t[p].tag == -233) return;
t[p*2].tag = t[p*2+1].tag = t[p].tag;
t[p*2].max = t[p*2+1].max = t[p].tag;
t[p*2].sum = (t[p*2].r-t[p*2].l+1)*t[p].tag;
t[p*2+1].sum = (t[p*2+1].r-t[p*2+1].l+1)*t[p].tag;
t[p].tag = -233;
return;
}
int ask(int p,int l,int r)
{
if (l <= t[p].l && r >= t[p].r) return t[p].sum;
int mid = t[p].l + t[p].r >> 1, sum = 0;
spread(p);
if (l <= mid) sum += ask(p*2,l,r);
if (r > mid) sum += ask(p*2+1,l,r);
return sum;
}
int find(int p,int l,int r,int v)
{
if (t[p].l == t[p].r) return t[p].l;
int mid = t[p].l+t[p].r >> 1;
spread(p);
if (l <= mid && t[p*2].max >= v) return find(p*2,l,r,v);
else if (r > mid && t[p*2+1].max >= v) return find(p*2+1,l,r,v);
else return n+1;
}
void change(int p,int l,int r,int v)
{
if (l <= t[p].l && r >= t[p].r) {
t[p].tag = t[p].max = v;
t[p].sum = (t[p].r-t[p].l+1) * v;
return;
}
spread(p);
int mid = t[p].l + t[p].r >> 1;
if (l <= mid) change(p*2,l,r,v);
if (r > mid) change(p*2+1,l,r,v);
t[p].max = max(t[p*2].max,t[p*2+1].max);
t[p].sum = t[p*2].sum + t[p*2+1].sum;
return;
}
int sum(int l,int r)
{
return ask(1,l,r) + S[r-1] - S[l-2];
}
//求解原序列区间和
void add(int x,int v)
{
int val = ask(1,x,x) + v;//得到bi修改后的值
int t = find(1,x+1,n,val)-1;//找到第一个大于等于bi的下表
change(1,x,t,val);//将找到的范围全部改成修改后的值
return;
}
signed main(void)
{
scanf("%lld", &n);
for (int i=1;i<=n;++i) scanf("%lld", a+i);
for (int i=1;i<n;++i) scanf("%lld", k+i);
for (int i=1;i<=n;++i) c[i] = c[i-1]+k[i];
for (int i=1;i<=n;++i) b[i] = a[i]-c[i-1];
for (int i=1;i<=n;++i) S[i] = c[i]+S[i-1];
build(1,1,n);
scanf("%lld", &Q);
while (Q --) {
char c = getchar();int x, y;
while (c ^ 's' && c ^ '+') c = getchar();
scanf("%lld %lld", &x, &y);
if (c == 's') printf("%lld\n", sum(x,y));
else add(x,y);
}
return 0;
}