• BZOJ 2006 超级钢琴(堆+主席树)


    很好的一道题。

    题意:给出长度为n的数列,选择k个互不相同的区间,满足每个区间长度在[L,R]内,求所有选择的区间和的总和最大是多少。(n,k<=5e5).

    首先将区间和转化为前缀和之差,那么我们想要区间和的总和最大,一个朴素的想法是把所有满足限制的区间和排序,取最大的k个。

    考虑每个右端点i,其中有效的左端点是[i-R+1,i-L+1].如果我们对于每个右端点都找到“当前”最大的区间和,那么把它们扔进大根堆里维护一下即可。

    由于sum[i]一定,那么只要左端点的sum值越小越好,于是我们每次从大根堆里弹出一个数,再从弹出的这个值的右端点出发,找一个次大的值。

    这样从大根堆里面弹出k次就是最大的k个区间和了。

    那么我们需要维护一个数据结构支持查询静态区间第k小,显然主席树可以满足这个要求。

    # include <cstdio>
    # include <cstring>
    # include <cstdlib>
    # include <iostream>
    # include <vector>
    # include <queue>
    # include <stack>
    # include <map>
    # include <bitset>
    # include <set>
    # include <cmath>
    # include <algorithm>
    using namespace std;
    # define lowbit(x) ((x)&(-x))
    # define pi acos(-1.0)
    # define eps 1e-8
    # define MOD 1000000007
    # define INF 1000000000
    # define mem(a,b) memset(a,b,sizeof(a))
    # define FOR(i,a,n) for(int i=a; i<=n; ++i)
    # define FO(i,a,n) for(int i=a; i<n; ++i)
    # define bug puts("H");
    # define lch p<<1,l,mid
    # define rch p<<1|1,mid+1,r
    # define mp make_pair
    # define pb push_back
    typedef pair<int,int> PII;
    typedef vector<int> VI;
    # pragma comment(linker, "/STACK:1024000000,1024000000")
    typedef long long LL;
    int Scan() {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const int N=500005;
    //Code begin...
    
    int a[N], sum[N], vis[N];
    struct qnode{
        int id, val;
        qnode(int _id=0, int _val=0):id(_id),val(_val){}
        bool operator<(const qnode &r)const{return val<r.val;}
    };
    priority_queue<qnode>que;
    VI v;
    int root[N], s[N*80], ls[N*80], rs[N*80], sz;
    
    void insert(int l, int r, int x, int &y, int val){
        y=++sz; s[y]=s[x]+1;
        if (l==r) return ;
        ls[y]=ls[x]; rs[y]=rs[x];
        int mid=(l+r)>>1;
        if (val<=mid) insert(l,mid,ls[x],ls[y],val);
        else insert(mid+1,r,rs[x],rs[y],val);
    }
    int query(int l, int r, int x, int y, int L)
    {
        if (l==r) return l;
        int mid=(l+r)>>1, val=s[ls[y]]-s[ls[x]];
        if (val>=L) return query(l,mid,ls[x],ls[y],L);
        return query(mid+1,r,rs[x],rs[y],L-val);
    }
    int main ()
    {
        int n, k, L, R;
        qnode tmp;
        scanf("%d%d%d%d",&n,&k,&L,&R);
        FOR(i,1,n) scanf("%d",a+i), sum[i]=sum[i-1]+a[i], v.pb(sum[i]);
        v.pb(0);
        sort(v.begin(),v.end());
        int siz=unique(v.begin(),v.end())-v.begin();
        FOR(i,1,n+1) {
            int x=lower_bound(v.begin(),v.begin()+siz,sum[i-1])-v.begin()+1;
            insert(1,siz,root[i-1],root[i],x);
        }
        FOR(i,1,n) {
            int l=max(i-R,0), r=i-L;
            if (r<l) continue;
            int x=query(1,siz,root[l],root[r+1],1); que.push(qnode(i,sum[i]-v[x-1]));
        }
        int num=0;
        LL ans=0;
        while (num<k) {
            tmp=que.top(); que.pop(); ans+=tmp.val; ++vis[tmp.id]; ++num;
            int l=max(tmp.id-R,0), r=tmp.id-L;
            if (r-l<vis[tmp.id]) continue;
            int x=query(1,siz,root[l],root[r+1],vis[tmp.id]+1); que.push(qnode(tmp.id,sum[tmp.id]-v[x-1]));
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    hdu3457(有向图的dp问题)
    nyoj16矩形嵌套(第一道dp关于dag的题目)
    noj1475(递推题)统计多少个1
    hdu1331(记忆化搜索)
    hdu1142(dj+记忆化搜索)
    hdu1978(记忆化搜索)
    用广搜实现的spfa
    hdu1428(记忆化搜索)
    hdu1078(记忆化搜索)
    poj3261(后缀数组)
  • 原文地址:https://www.cnblogs.com/lishiyao/p/6881687.html
Copyright © 2020-2023  润新知