• NOI2010 超级钢琴


    题目链接:戳我

    RMQ版本

    就是我们考虑记录一个三元组qwq,一个是pos,一个是l,一个是r。
    我们可以用ST表来查询固定左端点,右端点在一段区间内的最大值所在的位置。

    然后我们使用优先队列,不断累加最大值,然后弹出,求它的区间的子区间内的最大值。

    代码如下:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define MAXN 500010
    using namespace std;
    int n,l,r,k,cnt;
    long long ans;
    int log2[MAXN],st[MAXN][21],sum[MAXN],a[MAXN];
    struct Node{
        int st,l,r,maxpos;
        friend bool operator < (struct Node x,struct Node y)
            {return (sum[x.maxpos]-sum[x.st-1])<(sum[y.maxpos]-sum[y.st-1]);}
    };
    priority_queue<Node>q;
    inline void init()
    {
        for(int i=1;i<=20;i++)
        {
            for(int j=1;j+(1<<i)-1<=n;j++)
            {
                int cur1=st[j][i-1];
                int cur2=st[j+(1<<(i-1))][i-1];
                if(sum[cur1]>sum[cur2]) st[j][i]=cur1;
                else st[j][i]=cur2;
            }
        }
    }
    inline int query(int l,int r)
    {
        if(l==r) return l;
        int cur1=st[l][log2[r-l+1]];
        int cur2=st[r-(1<<log2[r-l+1])+1][log2[r-l+1]];
        if(sum[cur1]>sum[cur2]) return cur1;
        else return cur2;
    }
    inline void solve()
    {
        for(int i=1;i<=n;i++)
        {
            int ll=i+l-1,rr=min(n,i+r-1);
            if(ll>n) continue;
            q.push((Node){i,ll,rr,query(ll,rr)});
            // printf("%d %d %d %d
    ",i,ll,rr,q.top().maxpos);
        }
        while(!q.empty()&&cnt<k)
        {
            Node u=q.top(); q.pop();
            // printf("maxpos=%d
    ",u.maxpos);
            ans+=sum[u.maxpos]-sum[u.st-1];
            cnt++;
            if(u.maxpos-1>=u.l) q.push((Node){u.st,u.l,u.maxpos-1,query(u.l,u.maxpos-1)});
            if(u.maxpos+1<=u.r) q.push((Node){u.st,u.maxpos+1,u.r,query(u.maxpos+1,u.r)});
        }
        printf("%lld
    ",ans);
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        #endif
        scanf("%d%d%d%d",&n,&k,&l,&r);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++) st[i][0]=i;
        for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
        // for(int i=1;i<=n;i++) printf("sum[%d]=%d
    ",i,sum[i]);
        log2[0]=-1;
        for(int i=1;i<=n;i++) log2[i]=log2[i>>1]+1;
        // for(int i=1;i<=n;i++) printf("log2[%d]=%d
    ",i,log2[i]);
        init();
        solve();
        return 0;
    }
    

    主席树版本

    也是维护一个三元组。
    因为求的是一个连续的区间和,所以我们可以先做一下前缀和,然后固定右端点,在可选区间中选择最小的即可。
    就是记录一下在这个pos为右端点的区间中,已经取到了第多少qwq
    维护取第K大就是一个静态区间求第K的主席树经典应用qwq
    然后放进堆里即可。每次取出最大的,然后放入以这个为右端点,的K+1大值。
    记得要先离散化一下qwq,还有一直取到头(也就是前缀和减0)的情况——其实就是离散化的时候往数组里面放一个0即可。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #define MAXN 500010
    using namespace std;
    int n,k,L,R,top,tot;
    int pre[MAXN],rt[MAXN],sum[MAXN];
    long long ans;
    struct T{int siz,val,ls,rs;}t[MAXN<<5];
    struct Node
    {
        int pos,v,cnt;
        friend bool operator < (struct Node x,struct Node y) 
            {return x.v<y.v;}
    };
    priority_queue<Node>q;
    inline void modify(int &x,int f,int l,int r,int k)
    {
        x=++tot;
        t[x]=t[f],t[x].siz++;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(k<=mid) modify(t[x].ls,t[f].ls,l,mid,k);
        else modify(t[x].rs,t[f].rs,mid+1,r,k);
    }
    inline int query(int x,int f,int l,int r,int k)
    {
        if(l==r) return l;
        int cur=t[t[x].ls].siz-t[t[f].ls].siz;
        int mid=(l+r)>>1;
        if(k<=cur) return query(t[x].ls,t[f].ls,l,mid,k);
        else return query(t[x].rs,t[f].rs,mid+1,r,k-cur);
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        #endif
        scanf("%d%d%d%d",&n,&k,&L,&R);
        n++;
        for(int i=2;i<=n;i++) 
            scanf("%d",&sum[i]),sum[i]=sum[i-1]+sum[i],pre[++top]=sum[i];
        pre[++top]=0;
        sort(&pre[1],&pre[top+1]);
        top=unique(&pre[1],&pre[top+1])-pre-1;
        for(int i=1;i<=n;i++) sum[i]=lower_bound(&pre[1],&pre[top+1],sum[i])-pre;
        for(int i=1;i<=n;i++) modify(rt[i],rt[i-1],1,top,sum[i]);
        for(int i=L+1;i<=n;i++)
        {
            int pos=query(rt[max(0,i-L)],rt[max(0,i-R-1)],1,top,1);
            q.push((Node){i,pre[sum[i]]-pre[pos],1});
        }
        while(k--)
        {
            Node now=q.top();q.pop();
            now.cnt++;
            ans+=now.v;
            // printf("ans=%lld
    ",ans);
            if(now.cnt>min(max(0,now.pos-L),R-L+1)) continue;
            int cur=query(rt[max(0,now.pos-L)],rt[max(0,now.pos-R-1)],1,top,now.cnt);
            now.v=pre[sum[now.pos]]-pre[cur];
            q.push(now);
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    VSCode显示多个Tab窗口
    react + antd实现动态菜单
    vue 全局插件封装--提示toast
    ElementUI之el-scrollbar+el-select组合
    vue 滚动条组件对比
    【智能车】NXP_MIMXRT1064库函数
    【模电学习】二极管的特性与参数
    【模电学习】半导体——N与P(2)
    【协议】IIC通信
    【模电学习】半导体——N与P(1)
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10672664.html
Copyright © 2020-2023  润新知