zoukankan      html  css  js  c++  java
  • 【洛谷P2048】超级钢琴【堆】【主席树】

    题目大意:

    题目链接:https://www.luogu.org/problemnew/show/P2048
    一个长度为nn的序列,求mm个长度在[l,r][l,r]之间的子序列,使得这些子序列的元素之和最大。


    思路:

    显然暴力是很难搞的,考虑先用前缀和。
    那么我们要求的就是
    i=1mmax(sum[k]sum[x1])(x+l1kx+r1)sum^{m}_{i=1}max(sum[k]-sum[x-1])(x+l-1leq kleq x+r-1)
    考虑拆成mm次询问。
    对于每一次询问,我们要求max(sum[k]sum[x1])max(sum[k]-sum[x-1])。显然对于固定的xxsum[x1]sum[x-1]也是固定的。所以我们只要求出max(sum[k])max(sum[k])即可。
    kk是有范围要求的。当左端点为xx时,右端点的区间是固定的。我们每次要在这个区间内求最大,然后再求次大,接下来求第三大。
    这显然就是一个静态区间第kk大的问题。直接主席树搞定就可以了。
    那么我们要把所有的点作为左端点,然后求出相应的右端点,并插入堆里。每次取最大值然后维护一下就可以了。
    其实这一步就是序列合并了。里面会讲解的更详细一些。
    这样初始化O(nlogn)O(nlog n),每次询问O(logn)O(log n),共mm次询问,总时间复杂度就是O(mlogn)O(mlog n)
    要开long longlong long


    代码:

    #include <map>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #define mp make_pair
    using namespace std;
    typedef long long ll;
    priority_queue<pair<ll,int> > q;
    
    const int N=500010,M=500010*25;
    int n,m,L,R,T,tot,root[N],num[N];
    ll sum[N],b[N],ans;
    
    struct Tree
    {
        int ls,rs,cnt;
    }tree[M];
    
    int build(int l,int r)
    {
        int p=++tot;
        if (l==r) return p;
        int mid=(l+r)/2;
        tree[p].ls=build(l,mid);
        tree[p].rs=build(mid+1,r);
        return p;
    }
    
    int insert(int now,int l,int r,int k)
    {
        int p=++tot;
        tree[p]=tree[now];
        tree[p].cnt++;
        if (l==r) return p;
        int mid=(l+r)/2;
        if (k<=mid) tree[p].ls=insert(tree[p].ls,l,mid,k);
            else tree[p].rs=insert(tree[p].rs,mid+1,r,k);
        return p;
    }
    
    int ask(int x,int y,int l,int r,int k)
    {
        if (l==r) return l;
        int mid=(l+r)/2,sum=tree[tree[y].ls].cnt-tree[tree[x].ls].cnt;
        if (sum>=k)
            return ask(tree[x].ls,tree[y].ls,l,mid,k);
        else return ask(tree[x].rs,tree[y].rs,mid+1,r,k-sum);
    }
    //以上主席树板子
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&L,&R);
        for (int i=1;i<=n;i++)
        {
        	int x;
            scanf("%d",&x);
            sum[i]=sum[i-1]+x;
            b[i]=sum[i];
        }
        sort(b+1,b+1+n); 
        T=unique(b+1,b+1+n)-b-1;
        root[0]=build(1,T);
        for (int i=1;i<=n;i++)
            root[i]=insert(root[i-1],1,T,lower_bound(b+1,b+1+T,sum[i])-b);
        for (int i=1;i<=n;i++)
        {
        	int l=i+L-1,r=min(i+R-1,n);
        	if (l>n) break;
        	ll x=b[ask(root[l-1],root[r],1,T,(r-l+1))];
        	q.push(mp(x-sum[i-1],i));
        	num[i]=1;
    	}
        while (m--)
        {
        	ans+=q.top().first;
            int i=q.top().second;
            q.pop();
            num[i]++;
            int l=i+L-1,r=min(i+R-1,n);
            if (l<=n&&num[i]<=r-l+1)
            {
          		ll x=b[ask(root[l-1],root[r],1,T,(r-l+1-num[i]+1))];
          		//静态区间第k大=静态区间第r-l+1-k+1小
        		q.push(mp(x-sum[i-1],i));
    		}
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    Python3 简单实现下载某个网站轮播图片
    java中i=i++的解析
    String、StringBuffer、StringBuilder的区别和解析
    java值传递和引用传递
    Java并发(多线程)
    Java Set集合(HashSet、TreeSet)
    Java泛型的基本介绍与使用
    linux自学
    移动端项目准备工作
    响应式布局概念
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998133.html
Copyright © 2011-2022 走看看