• [Codeforces 1053C] Putting Boxes Together


    Link:

    Codeforces 1053C 传送门

    Solution:

    先推出一个结论:

    最后必有一个点不动且其为权值上最中间的一个点

    证明用反证证出如果不在中间的点必有一段能用代价少的替代多的

    这样问题转换为求出区间第一个大于权值和一半的点,并求结果

    如果只考虑半边的结果为$sum_{i=1}^{mid} (pos[mid]-pos[i]-(mid-i))*w[i]$

    将$pos[i]$修改为$pos[i]-i$之后维护$sum pos[i]*w[i]$的和即可

    以上操作可以用两颗线段树来维护

    注意:

    1、维护$w[i]$时不能取模!

    2、对于求权值中间点可以二分+树状数组也可以直接在线段树上二分

    线段树上二分时注意如果已经在区间内查询值要不断更新

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define pb push_back
    #define mid ((l+r)>>1)
    #define lc k<<1,l,mid
    #define rc k<<1|1,mid+1,r
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=5e5+10,INF=1<<28,MOD=1e9+7;
    
    struct SGT
    {
        ll seg[MAXN<<2]; 
        //分清何时要取模 
        void Update(int a,ll x,int f,int k,int l,int r)
        {
            if(l==r)
            {seg[k]+=x;if(f) seg[k]%=MOD;return;}
            if(a<=mid) Update(a,x,f,lc);
            else Update(a,x,f,rc);
            seg[k]=seg[k<<1]+seg[k<<1|1];
            if(f) seg[k]%=MOD;
        }
        ll Query(int a,int b,int f,int k,int l,int r)
        {
            if(a<=l&&r<=b) return seg[k];
            ll ret=0;
            if(a<=mid) ret+=Query(a,b,f,lc);
            if(b>mid) ret+=Query(a,b,f,rc);
            return f?ret%MOD:ret;
        }
        //注意线段树二分的处理及此处的引用 
        int Find(int a,int b,ll &x,int k,int l,int r)
        {
            if(a<=l&&r<=b)
            {
                if(seg[k]<x){x-=seg[k];return INF;}
                if(l==r) return l;
                if(x<=seg[k<<1]) return Find(a,b,x,lc);
                else{x-=seg[k<<1];return Find(a,b,x,rc);}
            }
            int ret=INF;
            if(a<=mid)
                ret=min(ret,Find(a,b,x,lc));
            if(b>mid&&ret==INF) 
                ret=min(ret,Find(a,b,x,rc));
            return ret;
        }
    }sw,sm;
    int n,q,x,y,pos[MAXN],w[MAXN];
    
    int main()
    {
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%d",&pos[i]),pos[i]-=i;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&w[i]);
            sw.Update(i,w[i],0,1,1,n);
            sm.Update(i,1ll*w[i]*pos[i],1,1,1,n);
        }
        
        while(q--)
        {
            scanf("%d%d",&x,&y);
            if(x<0)
            {
                x=-x;
                sw.Update(x,-w[x],0,1,1,n);
                sm.Update(x,-1ll*w[x]*pos[x],1,1,1,n);
                w[x]=y;
                sw.Update(x,w[x],0,1,1,n);
                sm.Update(x,1ll*w[x]*pos[x],1,1,1,n);
            }
            else
            {
                //此时不能取模 
                ll sum=(sw.Query(x,y,0,1,1,n)+1)/2;
                int p=sw.Find(x,y,sum,1,1,n);
                ll t1=(sw.Query(x,p,1,1,1,n)*pos[p]%MOD-sm.Query(x,p,1,1,1,n)+MOD)%MOD;
                ll t2=(-sw.Query(p+1,y,1,1,1,n)*pos[p]%MOD+sm.Query(p+1,y,1,1,1,n)+MOD)%MOD;
                printf("%lld
    ",(t1+t2)%MOD);
            }
        }
        return 0;
    }
  • 相关阅读:
    day10 Java学习(开发工具.Eclipse)
    day9 Java学习(面向对象Package关键字)
    day8 Java学习(面向对象.多态&接口)
    day7 Java学习(面向对象.继承)
    day5 Java学习(面向对象)
    day4 Java学习(数组)
    day3 Java学习(循环结构)
    day2 Java学习(位运算符)
    2019-05-26 java学习日记
    2019-05-25 java学习日记
  • 原文地址:https://www.cnblogs.com/newera/p/9705553.html
Copyright © 2020-2023  润新知