BZOJ 2006 NOI 2010 超级钢琴 堆+主席树

题目大意:给出一些音符,将它们组成和旋。和旋只能由[l,r]个音符组成。优美程度为所有音符的和。求k个和旋的又优美程度的最大和。

思路:先处理出来前缀和,以便O(1)去除一段的和。然后考虑对于一个音符来说,向左边扩展的音符是一段长度为r - l + 1的区间,取出的最大和是sum[i] - sum[p],sum[i]是一定的,要想让整段和最大,需要让sum[p]最小。之后就是区间k小值和堆得维护了,可以用时代的眼泪划分树,也可以用主席树。

CODE:

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 500010
#define RANGE 1000000010
using namespace std;
#define max(a,b) ((a) > (b) ? (a):(b))

struct SegTree{
    SegTree *son[2];
    int cnt;

    SegTree(SegTree *_,SegTree *__,int ___):cnt(___) {
        son[0] = _;
        son[1] = __;
    }
    SegTree() {}
}*_ver[MAX],**ver = _ver + 1;

struct Complex{
    int pos,val;

    Complex(int _,int __):pos(_),val(__) {}
    Complex() {}
    bool operator <(const Complex &a)const {
        return val < a.val;
    }
}heap[MAX];
int top;

int cnt,k,limit_min,limit_max;
int sum[MAX];

int now[MAX];

SegTree *BuildTree(SegTree *contrast,int l,int r,int x)
{
    if(l == r)  return new SegTree(NULL,NULL,contrast->cnt + 1);
    int mid = (l + r) >> 1;
    if(x <= mid) return new SegTree(BuildTree(contrast->son[0],l,mid,x),contrast->son[1],contrast->cnt + 1);
    return new SegTree(contrast->son[0],BuildTree(contrast->son[1],mid + 1,r,x),contrast->cnt + 1);
}

int Kth(SegTree *now,SegTree *contrast,int l,int r,int k)
{
    if(l == r)  return l;
    int mid = (l + r) >> 1;
    if(k <= now->son[0]->cnt - contrast->son[0]->cnt)    return Kth(now->son[0],contrast->son[0],l,mid,k);
    k -= (now->son[0]->cnt - contrast->son[0]->cnt);
    return Kth(now->son[1],contrast->son[1],mid + 1,r,k);
}

int main()
{
    cin >> cnt >> k >> limit_min >> limit_max;
    ver[-1] = new SegTree();
    ver[-1]->son[0] = ver[-1]->son[1] = ver[-1];
    ver[0] = BuildTree(ver[-1],-RANGE,RANGE,0);
    for(int i = 1; i <= cnt; ++i) {
        scanf("%d",&sum[i]);
        sum[i] += sum[i - 1];
        ver[i] = BuildTree(ver[i - 1],-RANGE,RANGE,sum[i]);
    }
    for(int i = limit_min; i <= cnt; ++i) {
        now[i] = 1;
        int _min = Kth(ver[i - limit_min],ver[max(-1,i - limit_max - 1)],-RANGE,RANGE,1);
        heap[++top] = Complex(i,sum[i] - _min);
    }
    make_heap(heap + 1,heap + top + 1);
    long long ans = 0;
    for(int i = 1; i <= k; ++i) {
        pop_heap(heap + 1,heap + top + 1);
        Complex temp = heap[top--];
        ans += temp.val;
        int r = temp.pos - limit_min,l = max(0,temp.pos - limit_max);
        if(++now[temp.pos] > r - l + 1)
            continue;
        int _min = Kth(ver[r],ver[l - 1],-RANGE,RANGE,now[temp.pos]);
        heap[++top] = Complex(temp.pos,sum[temp.pos] - _min);
        push_heap(heap + 1,heap + top + 1);
    }
    cout << ans << endl;
    return 0;
}

时间: 2024-10-25 11:58:05

BZOJ 2006 NOI 2010 超级钢琴 堆+主席树的相关文章

●BZOJ 2006 NOI 2010 超级钢琴

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2006 题解: RMQ + 优先队列 (+ 前缀) 记得在一两个月前,一次考试考了这个题目的简化版:序列中只有正整数.当时我们曰 :有负数的话就怕是莫得解法哦.然后,有负数的情况居然是NOI题...难哭. 1).首先尝试固定区间的右端点R.那么可取的左端点的范围就已经确定.所以对于右端点为 R的权和最大的区间就能够求出来了:先求出前缀序列 pre[],由于 sum = pre[R]-pre[

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

很好的一道题. 题意:给出长度为n的数列,选择k个互不相同的区间,满足每个区间长度在[L,R]内,求所有选择的区间和的总和最大是多少.(n,k<=5e5). 首先将区间和转化为前缀和之差,那么我们想要区间和的总和最大,一个朴素的想法是把所有满足限制的区间和排序,取最大的k个. 考虑每个右端点i,其中有效的左端点是[i-R+1,i-L+1].如果我们对于每个右端点都找到"当前"最大的区间和,那么把它们扔进大根堆里维护一下即可. 由于sum[i]一定,那么只要左端点的sum值越小越好

[NOI 2010]超级钢琴

Description 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙 的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符的美妙度为Ai,其中Ai可正可负. 一个"超级和弦"由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R.我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和.两个超级和 弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的. 小Z决定创作一首由k个超级和弦组成的乐曲,为了

【BZOJ 2006】[NOI2010]超级钢琴 ST

我们先把所有最左端对应的最优右端入堆,eg: z  在[l,r](由题目给出的L,R决定)之间的最优解 y,然后出堆以后,再入堆z,y-1,z,y+1,那么我们只需要用st找最大前缀和就好了(ST是一种用来解决RMQ问题的方法他的应用也就限于此了) #include <cstdio> #include <cstring> #include <queue> #define make(a,b,c,d) (DT){a,b,c,d} #define MAXN 500000 us

Bzoj 2006: [NOI2010]超级钢琴 堆,ST表

2006: [NOI2010]超级钢琴 Time Limit: 20 Sec  Memory Limit: 552 MBSubmit: 2222  Solved: 1082[Submit][Status][Discuss] Description 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符的美妙度为Ai,其中Ai可正可负. 一个“超级和弦”由若干个编号连续的音符组成,包含

bzoj 4504: K个串【大根堆+主席树】

像超级钢琴一样把五元组放进大根堆,每次取一个出来拆开,(d,l,r,p,v)表示右端点为d,左端点区间为(l,r),最大区间和值为v左端点在p上 关于怎么快速求区间和,用可持久化线段树维护(主席树?)每个点到他root的区间和,这样每次右端点右移就是上一个的线段树在(la[a[i]]+1,i)加上a[i],la是这个值a[i]上一次出现的位置 然后就可以在线处理询问了 有一点因为这个线段树建的是1~n,所以右端点不是n的时候取max会取到右端点向右还是初始值0的位置(有可能前面是负数),这样的解

BZOJ 1878 [SDOI2009]HH的项链 (主席树 或 莫队算法)

题目链接  HH的项链 这道题可以直接上主席树的模板 #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 5e4 + 10; const int M = 3e6 + 10

【BZOJ 3626】 [LNOI2014]LCA【在线+主席树+树剖】

题目链接: TP 题解:   可能是我比较纱布,看不懂题解,只好自己想了…… 我们考虑对于询问区间是可以差分的,然而这并没有什么卵用,然后考虑怎么统计答案. 首先LCA一定是z的祖先(这里说的祖先包括自己,以下祖先均为此概念)节点,也就是是说我们只要计算出每个祖先节点的贡献就可以了,再考虑每个祖先的贡献如何计算. 我们发现对于深度其实是该点到root的路径点数,所以我们可以这样想,我们询问z的祖先的答案,就是在计算有对于给定区间有多少个点经过了z的祖先. 那么思路到这里就很清晰了,我们先把每个点

BZOJ 2007 NOI 2010 海拔 平面图最小割-&gt;最短路SPFA+pq

题目大意:给出一个城市各个道路的双向流量,城市的左上角的高度是0,城市的右下角的高度是1,若人流升高海拔就会消耗体力,问最小需要消耗多少体力. 思路:这道题才是真正的让我见识到了algorithm中的heap的强大. 分析这道题可以发现,一定会有一条分界线,这个分界线左边高度都为0,右边高度都是1,然后找到这条分界点就可以了.明显的最小割.但是数据量巨大,直接跑最大流会T,又是平面图,建立对偶图然后跑最短路,SPFA+pq在BZOJ上可以很快,如果有的OJ卡STL的话可以考虑SPFA+Heap,