Codeforces Round #546 (Div. 2)E. Nastya Hasn't Written a Legend

Codeforces Round #546 (Div. 2) https://codeforces.com/contest/1136/problem/E

这个题思路还是很好想的,稍微推导下即可。
简单说下题意:
给你一个长度为 n n n的序列 a a a,和一个长度为 n − 1 n-1 n1的序列 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 &lt; i ≤ n ) a_i = max(a_i,a_{i-1}+k_{i-1})(x &lt; i\le n) ai=max(ai,ai1+ki1)(x<in)
    保证初始时满足: a i − 1 + k i − 1 ≤ a i a_{i-1}+k_{i-1}\le a_i ai1+ki1ai

注意到这个题的一个重要的性质:在对其修改操作时,假定在 p p p位置时 a p − 1 + k p − 1 ≤ a p a_{p-1}+k_{p-1} \le a_p ap1+kp1ap,那么 a a a序列中 p p p n n n的值都不会改变。
也就是说,修改区间为 [ x , p − 1 ] [x,p-1] [x,p1]
具体修改的值,推导下:

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} ap1=ap2+kp2

将上述式子加起来,得到: 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(i1,x) ( x ≤ i ≤ p − 1 ) (x\le i \le p-1) (xip1) s u m k sumk sumk表示 k k k序列的前缀和。
现在得到一个修改式:对 [ x , p − 1 ] [x,p-1] [x,p1]赋值为 a x + y + s u m k ( i − 1 , x ) a_x+y+sumk(i-1,x) ax+y+sumk(i1,x),化简下 a i = X + s u m k ( i − 1 ) a_i = X + sumk(i-1) ai=X+sumk(i1)
X = a x + y − s u m k ( x − 1 ) X = a_x+y-sumk(x-1) X=ax+ysumk(x1)

因为维护的是区间和,线段树的经典应用,利用懒标记的思想即可维护。

现在考虑怎样得到 p p p
x + 1 ≤ i ≤ p − 1 x+1\le i\le p-1 x+1ip1 满足 a i &lt; a i − 1 + k i − 1 a_i &lt; a_{i-1}+k_{i-1} ai<ai1+ki1
i = p i = p i=p 满足 a p − 1 + k p − 1 ≤ a p a_{p-1}+k_{p-1}\le a_p ap1+kp1ap
a p − 1 = a x + y + s u m k ( p − 2 , x ) a_{p-1} = a_x+y+sumk(p-2,x) ap1=ax+y+sumk(p2,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(p2,x)+kp1ap
整理: 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+ysumk(x1)apsumk(p1)
发现左边只和每次的询问有关,右边是一个和位置有关的函数,在线段树上维护下 b i = a i − s u m k ( i − 1 ) b_i = a_i-sumk(i-1) bi=aisumk(i1)的最大值即可,相当于在 [ x , n ] [x,n] [x,n]中寻找第一个比 a x + y − s u m k ( x − 1 ) a_x+y-sumk(x-1) ax+ysumk(x1)大的位置即可。

到这里这个题还有一点问题,在下传懒标记时,怎么维护 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(i1,x) ; b i = a i − s u m k ( i − 1 ) b_i = a_i-sumk(i-1) bi=aisumk(i1)
得到 b i = a x + y − s u m k ( x − 1 ) b_i = a_x+y - sumk(x-1) bi=ax+ysumk(x1)这是一个定值。也就是说这段区间 b i b_i bi的最大值为 a x + y − s u m k ( x − 1 ) a_x+y - sumk(x-1) ax+ysumk(x1)

完。

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值