zoukankan      html  css  js  c++  java
  • 【BZOJ5319】军训列队(JSOI2018)-主席树+二分

    测试地址:军训列队
    做法:本题需要用到主席树+二分。
    首先可以证明,最好的匹配方法是,按照区间内权值顺序从小到大依次匹配[K,K+rl]。证明如下:
    假设区间内有两个数x<y分别匹配上了i+1i,那么:
    x<yii+1x<y时,交换彼此的匹配后,距离和不发生变化;
    xi<i+1y时,交换彼此的匹配后,距离和减少2
    因此发生逆序时交换答案总会变优,于是显然没有逆序的匹配是最优的。
    现在要求距离和,因为a序列中的值互不相同,那么区间中的数在排序后,是单调递增的,而[K,K+rl]显然也是递增的,斜率为1,显然a的增长速度一定比[K,K+rl]的增长速度快(至少也不慢),所以一旦某个时刻a中某数超过了它对应的[K,K+rl]中的数,后面的时刻中a中的数就不可能再小于等于它们对应的[K,K+rl]中的数。
    于是两个序列中对位之差会从负,到0,再到正,于是我们需要尝试找到差值正负的分界点,在分界点两侧就可以简单地求和了。
    我们使用主席树处理过类似的询问,但是这里有一个问题:一个区间中第k大的数是多少,除非我们搜索时走到了其对应的叶子节点,不然我们不可能通过标记计算出这个数值。而O(nlog2n)的做法显然无法接受,那怎么办呢?
    实际上,我们完全没有必要把排名这个信息束缚在已出现在区间内的数上,而是一开始就对所有数定义一个“排名”num,表示小于等于该数的数中,有多少个在区间中出现,这个信息显然可以在主席树上求得。显然此时差值也是单调递增的,而且分界点也恰好在原先该分界的地方(因为对于在区间中出现的数,差值还是原值)。那么此时我们要找的分界点就是一个最大的,满足与K+num1的差值非负的数,于是我们在主席树上二分即可求出分界点,时间复杂度O(nlogn),这样我们就解决了这一题。
    这道题目告诉我,在做题时先不要胡乱规约问题,尽管有时候会起到使目前要解决的问题更清晰的作用,然而如果规约出的问题和原来的问题范围不太一致,死钻规约后的问题可能反而没有结果,要用放缩的角度看待问题。就这样吧。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,a[500010],ql,qr;
    int rt[500010],tot=0,ch[10000010][2]={0};
    ll qk,qsum,qnum;
    ll num[10000010]={0},sum[10000010]={0};
    ll presum[500010]={0};
    struct forsort
    {
        int id;
        ll val;
    }f[500010];
    
    bool cmp(forsort a,forsort b)
    {
        return a.val<b.val;
    }
    
    void pushup(int no)
    {
        num[no]=num[ch[no][0]]+num[ch[no][1]];
        sum[no]=sum[ch[no][0]]+sum[ch[no][1]];
    }
    
    void insert(int &no,int last,int l,int r,int x)
    {
        no=++tot;
        num[no]=num[last];
        sum[no]=sum[last];
        ch[no][0]=ch[last][0];
        ch[no][1]=ch[last][1];
        if (l==r)
        {
            num[no]++;
            sum[no]+=f[l].val;
            return;
        }
        int mid=(l+r)>>1;
        if (x<=mid) insert(ch[no][0],ch[last][0],l,mid,x);
        else insert(ch[no][1],ch[last][1],mid+1,r,x);
        pushup(no);
    }
    
    bool query(int no,int last,int l,int r)
    {
        if (f[r].val<=qk+qnum+num[no]-num[last]-1ll)
        {
            qsum=qsum+sum[no]-sum[last];
            qnum=qnum+num[no]-num[last];
            return 1;
        }
        if (l==r) return 0;
        int mid=(l+r)>>1;
        bool flag;
        flag=query(ch[no][0],ch[last][0],l,mid);
        if (flag) query(ch[no][1],ch[last][1],mid+1,r);
        return 0;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            f[i].id=i;
            scanf("%lld",&f[i].val);
            presum[i]=presum[i-1]+f[i].val;
        }
        sort(f+1,f+n+1,cmp);
    
        for(int i=1;i<=n;i++)
            a[f[i].id]=i;
    
        for(int i=1;i<=n;i++)
            insert(rt[i],rt[i-1],1,n,a[i]);
    
        for(int i=1;i<=m;i++)
        {
            qsum=qnum=0;
            scanf("%d%d%lld",&ql,&qr,&qk);
            query(rt[qr],rt[ql-1],1,n);
            ll ans=0;
            ans+=qnum*qk+qnum*(qnum-1ll)/2ll-qsum;
            ans+=presum[qr]-presum[ql-1]-qsum-(qr-ql+1ll-qnum)*qk;
            ans-=(qnum+qr-ql)*(qr-ql-qnum+1ll)/2ll;
            printf("%lld
    ",ans);
        }
    
        return 0;
    }
  • 相关阅读:
    <译>Spark Sreaming 编程指南
    <译>Zookeeper官方文档
    <译>Flink官方文档-Flink概述
    <译>流计算容错
    <译>Flink编程指南
    <续>调度算法补充
    storm源码阅读笔记之任务调度算法
    海量数据处理方法归类
    storm中worker、executor、task之间的关系
    javax.swing.jFrame
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793281.html
Copyright © 2011-2022 走看看