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;
    }
    
  • 相关阅读:
    多重背包POJ1276不要求恰好装满 poj1014多重背包恰好装满
    哈理工1053完全背包
    求最小公倍数与最大公约数的函数
    Bus Pass ZOJ 2913 BFS 最大中取最小的
    POJ 3624 charm bracelet 01背包 不要求装满
    HavelHakimi定理(判断一个序列是否可图)
    z0j1008Gnome Tetravex
    ZOJ 1136 Multiple BFS 取模 POJ 1465
    01背包 擎天柱 恰好装满 zjut(浙江工业大学OJ) 1355
    zoj 2412 水田灌溉,求连通分支个数
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998133.html
Copyright © 2011-2022 走看看