zoukankan      html  css  js  c++  java
  • Codeforces 1136E

    题目链接:https://codeforces.com/problemset/problem/1136/E

    题意:

    给出一个 $a[1 sim n]$,以及一个 $k[1 sim (n-1)]$,初始保证所有的 $1 le i le n-1$ 都满足 $a[i]+k[i] le a[i+1]$。

    现在有两种操作:

      第一种是令指定的 $a[i]$ 加上一个非负整数 $x$,此时若有 $a[i] + k[i] > a[i+1]$,则 $a[i+1]$ 变为 $a[i] + k[i]$,往后依次类推。

      第二种是给定一个区间 $[l,r]$ 求 $a[l] + cdots + a[r]$。

    题解:

    首先,我们知道对一个 $a[i]$ 加上 $x$ 后,根据其后面的 $a[i] + k[i] le a[i+1], a[i+1] + k[i+1] le a[i+2], cdots$ 的“松紧程度”的变化,$a[i]$ 加上 $x$ 其带来的影响会逐步减弱,直到在某个位置 $j$ 之后完全消失,这个 $a[j]$ 是最后一个要被修改的数。

    那么,如果我们找到了这个区间 $[i,j]$,我们现在要做的修改操作,就是要对这个区间进行一定的修改。

    不难发现,这个区间内的第一个数变成了 $a[i]+x$,第二个变成了 $a[i]+x+k[i]$,第三个变成了 $a[i]+x+k[i]+k[i+1]$,依次类推……

    考虑这个式子可以分为两部分:$a[i] + x$ 部分,以及 $k[i] + k[i+1] + cdots$ 部分。

    可以考虑分开维护这两个部分,前一部分很好维护,线段树区间更新;后一部分直接维护比较困难,我们可以这样维护:

    $k[i],k[i]+k[i+1],k[i]+k[i+1]+k[i+2],cdots$,不难看出是一个类似于 $k$ 数组的前缀和的求和,举个栗子:

    $k_3 ,: k_3+k_4 ,: k_3+k_4+k_5 ,: k_3+k_4+k_5+k_6$,如果是前缀和求和,那么应当是 $k_1+k_2+k_3 :,: k_1+k_2+k_3+k_4 :,: k_1+k_2+k_3+k_4+k_5 :,: k_1+k_2+k_3+k_4+k_5+k_6$。

    也就是说,要在前缀和上减掉 $4 imes (k_1+k_2)$,不难发现,这个值是比较好维护的,是对某一段区间直接赋值,所以可以用线段树维护这个东西。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll INF=1e18;
    const int maxn=1e5+10;
    int n,q;
    ll a[maxn],k[maxn],s[maxn];
    
    struct Node
    {
        int l,r;
        ll val,lazy;
        void update(ll x)
        {
            val=(r-l+1)*x;
            lazy=x;
        }
    };
    struct SegmentTree
    {
    #define ls (rt<<1)
    #define rs (rt<<1|1)
        Node o[maxn<<2];
        void pushdown(int rt)
        {
            if(o[rt].lazy!=-INF)
            {
                o[ls].update(o[rt].lazy);
                o[rs].update(o[rt].lazy);
                o[rt].lazy=-INF;
            }
        }
        void pushup(int rt)
        {
            o[rt].val=o[ls].val+o[rs].val;
        }
        void build(int rt,int l,int r,ll v[])
        {
            o[rt].l=l, o[rt].r=r;
            o[rt].lazy=-INF;
            if(l==r)
            {
                o[rt].val=v[l];
                return;
            }
            int mid=(l+r)>>1;
            build(ls,l,mid,v);
            build(rs,mid+1,r,v);
            pushup(rt);
        }
        void update(int rt,int st,int ed,ll val)
        {
            if(st<=o[rt].l && o[rt].r<=ed)
            {
                o[rt].update(val);
                return;
            }
            pushdown(rt);
            int mid=(o[rt].l+o[rt].r)>>1;
            if(st<=mid) update(ls,st,ed,val);
            if(mid<ed) update(rs,st,ed,val);
            pushup(rt);
        }
        ll query(int rt,int st,int ed)
        {
            if(st<=o[rt].l && o[rt].r<=ed) return o[rt].val;
            pushdown(rt);
            ll res=0;
            int mid=(o[rt].l+o[rt].r)>>1;
            if(st<=mid) res+=query(ls,st,ed);
            if(mid<ed) res+=query(rs,st,ed);
            return res;
        }
    }T[2];
    
    inline ll getval(int p)
    {
        return T[0].query(1,p,p)+k[p]-T[1].query(1,p,p);
    }
    void modify(int p,ll x)
    {
        int l=p, r=n;
        ll base=getval(p);
        while(l<r)
        {
            int mid=(l+r+1)>>1;
            if(base+x+k[mid]-k[p]>getval(mid)) l=mid;
            else r=mid-1;
        }
        T[0].update(1,p,r,base+x);
        T[1].update(1,p,r,k[p]);
    }
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0), cout.tie(0);
    
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
    
        k[1]=s[1]=0;
        for(int i=2;i<=n;i++) cin>>k[i], k[i]+=k[i-1], s[i]=s[i-1]+k[i];
    
        T[0].build(1,1,n,a);
        T[1].build(1,1,n,k);
    
        cin>>q;
        char op[2];
        while(q--)
        {
            cin>>op;
            if(op[0]=='+')
            {
                int p; ll x; cin>>p>>x;
                modify(p,x);
            }
            if(op[0]=='s')
            {
                int l,r; cin>>l>>r;
                ll res1=T[0].query(1,l,r);
                ll res2=s[r]-s[l-1]-T[1].query(1,l,r);
                cout<<res1+res2<<'
    ';
            }
        }
    }

    有一个注意点是懒标记初始化成负无穷。

  • 相关阅读:
    火眼金睛算法,教你海量短文本场景下去重
    CynosDB技术详解——架构设计
    CynosDB技术详解——存储集群管理
    解决 "Script Error" 的另类思路
    Go 语言实践(一)
    Vue.js的复用组件开发流程
    MYSQL中的COLLATE是什么?
    Blending
    AlphaTesting
    Culling & Depth Testing
  • 原文地址:https://www.cnblogs.com/dilthey/p/10561457.html
Copyright © 2011-2022 走看看