• [NOI2010]超级钢琴


    [NOI2010]超级钢琴

    提供两种写法

    Part 1 - > \(n log ^2 n\)

    \(k\)大问题常用思想,二分答案

    离散后用树状数组维护左边\(j \in [i-R,i-L]\)距离内的前缀和(\(Sum\))的值满足\(Sum_i-Sum_j \ge Ans\)的个数

    这是一种非常套路的写法

    最后还要统计总和,可以再增加一个差分来统计

    就统计 > \(Ans\) 的和,剩下的个数答案都是 \(Ans\),累加即可

    (下面这份代码经历了卡常,不具有可读性)

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    using namespace std;
     
    #define reg register
    typedef long long ll;
    #define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
    
     
    char IO;
    int rd(){
        int s=0,f=0;
        while(!isdigit(IO=getchar())) f|=(IO=='-');
        do s=(s<<1)+(s<<3)+(IO^'0');
        while(isdigit(IO=getchar()));
        return f?-s:s;
    }
     
    const int N=5e5+10,INF=1e9;
     
     
    int n,m,l,r;
    int s[N],h[N],hc;
    int sum[N];
    ll sum2[N];
     
    void Add(reg int p,reg int x) {
        while(p<=hc) sum[p]+=x,p+=p&-p;
    }
    void Add2(int p,int x,int y) {
        while(p<=hc) sum[p]+=x,sum2[p]+=y,p+=p&-p;
    }
     
    int Que(reg int p) {
        int res=0;
        while(p) res+=sum[p],p-=p&-p;
        return res;
    }
    ll Que2(int p) {
        ll res=0;
        while(p) res+=sum2[p],p-=p&-p;
        return res;
    }
     
    ll Count(int lim) {
        reg ll cnt=0;
        rep(i,1,hc) sum[i]=0;
        for(reg int i=l;i<=n;++i)  {
            ((h[s[i-l]]+lim<=h[hc])&&(Add(s[i-l],1),0));
            
            ((h[s[i]]>=h[1]+lim)&&(cnt+=Que(
            ({
            reg int l=1,r=hc,res=0;
            while(l<=r) {
                reg int mid=(l+r)>>1;
                h[mid]+lim<=h[s[i]]?(l=mid+1,res=mid):r=mid-1;
            }
            res;
            })
            )));  // 二分满足 s[i]-s[h[res]]>=lim
            ((i>=r && h[s[i-r]]+lim<=h[hc])&&(Add(s[i-r],-1),0));
        }
        return cnt;
    }
     
    int main(){
        n=rd(),m=rd(),l=rd(),r=rd();
        rep(i,1,n) s[i]=s[i-1]+rd();
        rep(i,0,n) h[++hc]=s[i];
        sort(h+1,h+hc+1),hc=unique(h+1,h+hc+1)-h-1;
        rep(i,0,n) s[i]=lower_bound(h+1,h+hc+1,s[i])-h;
        int l=-1e6,r=5e8,res=-1;
        while(l<=r) {
            int mid=(l+r)>>1;
            if(Count(mid)>=m) l=mid+1,res=mid;
            else r=mid-1;
        }
        res++;
        ll ans=0,cnt=0;
        memset(sum,0,sizeof sum);
        rep(i,::l,n) {
            Add2(s[i-::l],1,h[s[i-::l]]);
            int t=upper_bound(h+1,h+hc+1,h[s[i]]-res)-h-1;
            int c=Que(t);
            ans+=1ll*c*h[s[i]]-Que2(t);
            cnt+=c;
            if(i>=::r) Add2(s[i-::r],-1,-h[s[i-::r]]);
        }
        ans+=1ll*(m-cnt)*(res-1);
        printf("%lld\n",ans);
    }
    

    效率

    1.png

    \[\ \]

    \[\ \]

    Part2 - > \(nlogn\)

    考虑用堆+st表维护最大值

    对于每个\(i\),取\(j \in [i-R,i-L]\)\(Sum_j\)最小的位置加入堆,同时记录两个值\([l,r]=[i-R,i-L]\),每次弹出堆后,分别取\([l,j-1],[j+1,r]\)两段中的最优位置,重新加入堆

    最小值的位置用\(st\)表维护即可\(O(1)\)查询

    因为这题的\(k\)比较小,所以如此重复\(k\)次就能得到答案

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #include<iostream>
    using namespace std;
     
    #define reg register
    typedef long long ll;
    #define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
     
     
    char IO;
    int rd(){
        int s=0,f=0;
        while(!isdigit(IO=getchar())) f|=(IO=='-');
        do s=(s<<1)+(s<<3)+(IO^'0');
        while(isdigit(IO=getchar()));
        return f?-s:s;
    }
     
    const int N=5e5+10,INF=1e9;
     
     
    int n,m,L,R;
    int a[N],s[N][20],p[N][20];
    int Log[N];
    int Que(int l,int r) {
        int d=Log[r-l+1];
        return s[l][d]<s[r-(1<<d)+1][d]?p[l][d]:p[r-(1<<d)+1][d];
    }
     
    struct Node{
        int p,l,r,t,x;
        bool operator < (const Node __) const {
            return x<__.x;
        }
    };
    priority_queue <Node> Q;
     
    int main(){
        n=rd(),m=rd(),L=rd(),R=rd();
        rep(i,1,n) a[i]=a[i-1]+rd();
        rep(i,2,n) Log[i]=Log[i>>1]+1;
        drep(i,n,0) {
            s[i][0]=a[i],p[i][0]=i;
            for(reg int j=1;(1<<j)+i<=n+1;++j) {
                if(s[i][j-1]<s[i+(1<<(j-1))][j-1]) {
                    s[i][j]=s[i][j-1];
                    p[i][j]=p[i][j-1];
                } else {
                    s[i][j]=s[i+(1<<(j-1))][j-1];
                    p[i][j]=p[i+(1<<(j-1))][j-1];
                }
            }
        }
        ll ans=0;
        rep(i,L,n) {
            int t=Que(max(0,i-R),i-L);
            Q.push((Node){i,max(0,i-R),i-L,t,a[i]-a[t]});
        }
        rep(i,1,m) {
            Node t=Q.top(); Q.pop();
            ans+=t.x;
            if(t.l<t.t) {
                int p=Que(t.l,t.t-1);
                Q.push((Node){t.p,t.l,t.t-1,p,a[t.p]-a[p]});
            }
            if(t.t<t.r) {
                int p=Que(t.t+1,t.r);
                Q.push((Node){t.p,t.t+1,t.r,p,a[t.p]-a[p]});
            }
        }
        printf("%lld\n",ans);
    }
    

    代码写得比较low,但是效率差异显著

    1.png

  • 相关阅读:
    对抽象编程:接口和抽象类
    工厂方法模式
    用例建模Use Case Modeling
    分析一套源代码的代码规范和风格并讨论如何改进优化代码
    结合工程实践选题调研分析同类软件产品
    如何提高程序员的键盘使用效率
    Java复习面试指南01什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?
    Mac系统下MySql下载MySQL5.7及详细安装流程
    Java复习面试指南02JDK和JRE的区别?程序从源代码到运行经历哪几步?
    毕业半年小结
  • 原文地址:https://www.cnblogs.com/chasedeath/p/11824950.html
Copyright © 2020-2023  润新知