Codeforces Round #546 (Div. 2) https://codeforces.com/contest/1136/problem/E
这个题思路还是很好想的,稍微推导下即可。
简单说下题意:
给你一个长度为
n
n
n的序列
a
a
a,和一个长度为
n
−
1
n-1
n−1的序列
k
k
k。
现在有
m
m
m次操作:
- s x y s \ x\ y s x y 询问 a a a序列的 [ x , y ] [x,y] [x,y]的和。
-
+
x
y
+\ x\ y
+ x y将
a
[
x
]
a[x]
a[x]加上
y
y
y,然后使
a
i
=
m
a
x
(
a
i
,
a
i
−
1
+
k
i
−
1
)
(
x
<
i
≤
n
)
a_i = max(a_i,a_{i-1}+k_{i-1})(x < i\le n)
ai=max(ai,ai−1+ki−1)(x<i≤n)
保证初始时满足: a i − 1 + k i − 1 ≤ a i a_{i-1}+k_{i-1}\le a_i ai−1+ki−1≤ai。
注意到这个题的一个重要的性质:在对其修改操作时,假定在
p
p
p位置时
a
p
−
1
+
k
p
−
1
≤
a
p
a_{p-1}+k_{p-1} \le a_p
ap−1+kp−1≤ap,那么
a
a
a序列中
p
p
p到
n
n
n的值都不会改变。
也就是说,修改区间为
[
x
,
p
−
1
]
[x,p-1]
[x,p−1]。
具体修改的值,推导下:
a
x
=
a
x
+
y
a_x = a_x+y
ax=ax+y
a
x
+
1
=
a
x
+
k
x
a_{x+1}=a_{x}+k_x
ax+1=ax+kx
a
x
+
2
=
a
x
+
1
+
k
x
+
1
a_{x+2}=a_{x+1}+k_{x+1}
ax+2=ax+1+kx+1
⋯
⋯
\cdots \cdots
⋯⋯
a
p
−
1
=
a
p
−
2
+
k
p
−
2
a_{p-1}=a_{p-2}+k_{p-2}
ap−1=ap−2+kp−2
将上述式子加起来,得到:
a
i
=
a
x
+
y
+
s
u
m
k
(
i
−
1
,
x
)
a_i = a_{x}+y+sumk(i-1,x)
ai=ax+y+sumk(i−1,x)
(
x
≤
i
≤
p
−
1
)
(x\le i \le p-1)
(x≤i≤p−1)。
s
u
m
k
sumk
sumk表示
k
k
k序列的前缀和。
现在得到一个修改式:对
[
x
,
p
−
1
]
[x,p-1]
[x,p−1]赋值为
a
x
+
y
+
s
u
m
k
(
i
−
1
,
x
)
a_x+y+sumk(i-1,x)
ax+y+sumk(i−1,x),化简下
a
i
=
X
+
s
u
m
k
(
i
−
1
)
a_i = X + sumk(i-1)
ai=X+sumk(i−1);
X
=
a
x
+
y
−
s
u
m
k
(
x
−
1
)
X = a_x+y-sumk(x-1)
X=ax+y−sumk(x−1)。
因为维护的是区间和,线段树的经典应用,利用懒标记的思想即可维护。
现在考虑怎样得到
p
p
p。
当
x
+
1
≤
i
≤
p
−
1
x+1\le i\le p-1
x+1≤i≤p−1 满足
a
i
<
a
i
−
1
+
k
i
−
1
a_i < a_{i-1}+k_{i-1}
ai<ai−1+ki−1
当
i
=
p
i = p
i=p 满足
a
p
−
1
+
k
p
−
1
≤
a
p
a_{p-1}+k_{p-1}\le a_p
ap−1+kp−1≤ap
a
p
−
1
=
a
x
+
y
+
s
u
m
k
(
p
−
2
,
x
)
a_{p-1} = a_x+y+sumk(p-2,x)
ap−1=ax+y+sumk(p−2,x)
带入:
a
x
+
y
+
s
u
m
k
(
p
−
2
,
x
)
+
k
p
−
1
≤
a
p
a_x+y+sumk(p-2,x)+k_{p-1}\le a_p
ax+y+sumk(p−2,x)+kp−1≤ap
整理:
a
x
+
y
−
s
u
m
k
(
x
−
1
)
≤
a
p
−
s
u
m
k
(
p
−
1
)
a_x+y-sumk(x-1)\le a_p-sumk(p-1)
ax+y−sumk(x−1)≤ap−sumk(p−1)
发现左边只和每次的询问有关,右边是一个和位置有关的函数,在线段树上维护下
b
i
=
a
i
−
s
u
m
k
(
i
−
1
)
b_i = a_i-sumk(i-1)
bi=ai−sumk(i−1)的最大值即可,相当于在
[
x
,
n
]
[x,n]
[x,n]中寻找第一个比
a
x
+
y
−
s
u
m
k
(
x
−
1
)
a_x+y-sumk(x-1)
ax+y−sumk(x−1)大的位置即可。
到这里这个题还有一点问题,在下传懒标记时,怎么维护
b
i
b_i
bi的最大值。
在将标记下推时,这段区间是属于被修改的区间。那么对于
i
i
i(
i
i
i属于这段区间),
a
i
=
a
x
+
y
+
s
u
m
k
(
i
−
1
,
x
)
a_i = a_{x}+y+sumk(i-1,x)
ai=ax+y+sumk(i−1,x) ;
b
i
=
a
i
−
s
u
m
k
(
i
−
1
)
b_i = a_i-sumk(i-1)
bi=ai−sumk(i−1)
得到
b
i
=
a
x
+
y
−
s
u
m
k
(
x
−
1
)
b_i = a_x+y - sumk(x-1)
bi=ax+y−sumk(x−1)这是一个定值。也就是说这段区间
b
i
b_i
bi的最大值为
a
x
+
y
−
s
u
m
k
(
x
−
1
)
a_x+y - sumk(x-1)
ax+y−sumk(x−1)。
完。
#include<bits/stdc++.h>
using namespace std;
#define inf 1e18
const int maxn = 1e5+100;
typedef long long ll;
ll sumk1[maxn],sumk2[maxn],a[maxn],k[maxn];
struct segtree{
#define ls (rt<<1)
#define rs (rt<<1|1)
ll D[maxn<<2],sum[maxn<<2],mx[maxn<<2];
void push(int l,int r,int rt) {
if(D[rt]==inf) return ;
int mid = l+r>>1;
sum[ls] = sumk2[mid-1] + (mid-l+1)*D[rt];sum[rs] = sumk2[r-1]+(r-mid)*D[rt];
if(l>1) sum[ls] -= sumk2[l-2];
sum[rs] -= sumk2[mid-1];
D[ls] = D[rs] = D[rt];
mx[ls] = mx[rs] = D[rt];
D[rt] = inf;
}
void maintain(int rt)
{
sum[rt] = sum[ls] + sum[rs];
mx[rt] = max(mx[ls],mx[rs]);
}
void build(int l,int r,int rt) {
D[rt] = inf;
if(l==r) {
sum[rt] = a[l];
mx[rt] = a[l] - sumk1[l-1];
}else {
int mid=l+r>>1;
build(l,mid,ls);
build(mid+1,r,rs);
maintain(rt);
}
}
int find(int l,int r,int rt,int p,ll v) {
if(mx[rt]<v||r<p) return -1;
int mid=l+r>>1;
if(l==r) return l;
push(l,r,rt);
int x = find(l,mid,ls,p,v);
if(x==-1) x = find(mid+1,r,rs,p,v);
return x;
}
void update(int l,int r,int rt,int ql,int qr,ll c) {
if(ql<=l&&qr>=r) {
D[rt] = c;
sum[rt] = sumk2[r-1]-sumk2[l-2]+c*(r-l+1);
mx[rt] = c;
}else {
push(l,r,rt);
int mid=l+r>>1;
if(ql<=mid) update(l,mid,ls,ql,qr,c);
if(qr>mid) update(mid+1,r,rs,ql,qr,c);
maintain(rt);
}
}
ll query(int l,int r,int rt,int ql,int qr) {
if(ql<=l&&qr>=r) return sum[rt];
int mid=l+r>>1;
push(l,r,rt);
ll ret = 0;
if(ql<=mid) ret = query(l,mid,ls,ql,qr);
if(qr>mid) ret += query(mid+1,r,rs,ql,qr);
return ret;
}
}T;
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
int n,m;
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<n;++i) cin>>k[i];
for(int i=1;i<n;++i) sumk1[i] = sumk1[i-1] + k[i],sumk2[i] = sumk2[i-1]+sumk1[i];
T.build(1,n,1);
cin>>m;
while(m--){
char s[10];
int l,r;
cin>>s>>l>>r;
if(s[0]=='s') {
cout<<T.query(1,n,1,l,r)<<'\n';
}else {
ll v = T.query(1,n,1,l,l);
ll c = v + r - sumk1[l-1];
int g = T.find(1,n,1,l,c);
g--;
if(g==-2) g = n;
// cout<<g<<'\n';
T.update(1,n,1,l,g,c);
}
}
return 0;
}