bzoj-2006 [NOI2010]超级钢琴

2006 [NOI2010]超级钢琴
题目链接
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 3770 Solved: 1874
Description
小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的
音乐。 这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。 一个“超级
和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的
所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。
小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。
我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最
大值是多少。
Input
第一行包含四个正整数n, k, L, R。其中n为音符的个数,k为乐曲所包含的超级和弦个数,L和R分别是超级和弦所
包含音符个数的下限和上限。 接下来n行,每行包含一个整数Ai,表示按编号从小到大每个音符的美妙度。
N<=500,000
k<=500,000
-1000<=Ai<=1000,1<=L<=R<=N且保证一定存在满足条件的乐曲
Output
只有一个整数,表示乐曲美妙度的最大值。

Sample Input
4 3 2 3

3

2

-6

8
Sample Output
11

【样例说明】

共有5种不同的超级和弦:

音符1 ~ 2,美妙度为3 + 2 = 5

音符2 ~ 3,美妙度为2 + (-6) = -4

音符3 ~ 4,美妙度为(-6) + 8 = 2

音符1 ~ 3,美妙度为3 + 2 + (-6) = -1

音符2 ~ 4,美妙度为2 + (-6) + 8 = 4

最优方案为:乐曲由和弦1,和弦3,和弦5组成,美妙度为5 + 2 + 4 = 11。

题目大意
给出一个 n 个元素的序列,求 k 段长度介于 L 到 R 的不重复子段,使得这些子段上的元素加和最大。


题解
很自然地想到构造一下前缀和,然后对于每个 s[i] 前面的 s[i-R] 到 s[i-L] 中最小的。
s[i] 与之作差,即为以 i 为结尾的长度 L 到 R 的线段的最大值,然后从众多最大值中挑一个最大的,再修正堆中的元素。

方案一:ST表 + 堆
求区间极值,马上想到ST表算法,对于某区间最小的可以直接求得。
对于次小的,假设最小的位于 x ,那么次小的一定是min( s[L ~ x-1] , s[x+1 ~ R])。
其中 L和R表当前区间的左右边界。

那么只需把这两部分推进堆里就好了。

方案二:主席树 + 堆
如果没想到上面的方案,没关系。
我们每次查询可以看成超找区间第 k 大,可用主席树查找,再另开一个数组表示当前查找第几大。

代码
ST表 + 堆

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=500005;
const double lg2=log(2);
int n,k,L,R,f[maxn][20],a[maxn],tot,LG[maxn];
long long ans=0;
struct hp{
    int x,l,r,lst,s;
    bool operator <(const hp b){return s<b.s;}
    bool operator >(const hp b){return s>b.s;}
}h[maxn<<2],now;
int read()
{
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
void put(hp a){h[++tot]=a;push_heap(h+1,h+tot+1);}
hp get(){pop_heap(h+1,h+tot+1);return h[tot--];}
int cmp(int x,int y){return a[x]<a[y]?x:y;}
int getmx(int L,int R)
{
    if (L>R) return -1;
    int k=LG[R-L+1];
    return cmp(f[L][k],f[R-(1<<k)+1][k]);
}
int main()
{
    n=read()+1;k=read();L=read();R=read();
    f[1][0]=1;
    for (int i=2;i<=n;i++) a[i]=a[i-1]+read(),f[i][0]=i;
    for (int i=0;i<=n;i++) LG[i]=log(i)/lg2;
    int m=LG[n];
    for (int j=1;j<=m;j++)
     for (int i=1;i+(1<<j)-1<=n;i++)
        f[i][j]=cmp(f[i][j-1],f[i+(1<<j-1)][j-1]);
    for (int lst,i=L+1;i<=n;i++)
    {
        lst=getmx(max(i-R,1),i-L);
        put((hp){i,max(i-R,1),i-L,lst,a[i]-a[lst]});
    }
    for (int lst,i=1;i<=k;i++)
    {
        now=get();
        ans+=now.s;
        lst=getmx(now.l,now.lst-1);
        if (lst>0) put((hp){now.x,now.l,now.lst-1,lst,a[now.x]-a[lst]});
        lst=getmx(now.lst+1,now.r);
        if (lst>0) put((hp){now.x,now.lst+1,now.r,lst,a[now.x]-a[lst]});
    }
    printf("%lld",ans);
    return 0;
}

主席树 + 堆

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=500005;
int n,k,a[maxn],b[maxn],tot,num[maxn],RIGHT,LEFTT;
struct HHH{
    int x,id;
    //x表示权值,id表节点下表 
}h[maxn];
struct js{
    js *l;
    js *r;
    int s;
    js(){l=r=NULL;s=0;}
    //l和r表示左右儿子,s表示子树权值,查找第k大的节点
};
js *rot[maxn],*rt;

int read(){
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}

int fin(int x){
    for (int L=0,R=n,mid=L+R>>1;L<=R;mid=L+R>>1)
      if (x>b[mid]) L=mid+1;else
      if (x<b[mid]) R=mid-1;else
      return mid;
}

js* create(){js* x=new js;return x;}

void build(js *now,int L,int R){
    if (L==R) return;
    int mid=L+R>>1;
    build(now->l=create(),L,mid);
    build(now->r=create(),mid+1,R);
}

void add(js* now,js* lst,int x){
    now->s=lst->s+1;
    for (int L=0,R=n,mid=L+R>>1;L<R;mid=L+R>>1){
        if (x<=mid){
            now->l=create();
            now->r=lst->r;
            now=now->l;
            lst=lst->l;
            R=mid;
        }else{
            now->l=lst->l;
            now->r=create();
            now=now->r;
            lst=lst->r;
            L=mid+1;
        }
        now->s=lst->s+1;
    }
}

int query(js* Lrt,js* Rrt,int k){
    if (Rrt->s-Lrt->s<k) return 1000000000;
    for (int L=0,R=n,mid=L+R>>1;L<=R;mid=L+R>>1){
        if (k<=Rrt->l->s-Lrt->l->s){
            Lrt=Lrt->l;
            Rrt=Rrt->l;
            R=mid;
        }else{
            k-=Rrt->l->s-Lrt->l->s;
            Lrt=Lrt->r;
            Rrt=Rrt->r;
            L=mid+1;
        }
        if (L==R) return b[L];
    }
}

void put(int x,int id){
    h[++tot]=(HHH){x,id};
    for (int fa=tot;fa>>1>0;fa>>=1)
      if (h[fa>>1].x<h[fa].x) swap(h[fa>>1],h[fa]);
      else break;
}

js* ck(int x){if (x<0) return rt;else return rot[x];}

int get(){
    int ret=h[1].x,id=h[1].id,x=query(ck(max(id-RIGHT-1,-1)),rot[max(id-LEFTT,0)],++num[id]);
    if (x==1000000000) swap(h[1],h[tot]),tot--;else h[1].x=a[id]-x;
    for (int fa=1,son;fa<<1<=tot;fa=son){
        if ((fa<<1|1)>tot||h[fa<<1].x>h[(fa<<1|1)].x) son=fa<<1;else son=fa<<1|1;
        if (h[fa].x<h[son].x) swap(h[fa],h[son]);else break;
    }
    return ret;
}

int main(){
    n=read();k=read();LEFTT=read();RIGHT=read();
    for (int i=1;i<=n;i++) b[i]=a[i]=read()+a[i-1];
    sort(b,b+n+1);
    rt=create();
    for (int i=0;i<=n;i++) rot[i]=create();
    build(rt,0,n);
    add(rot[0],rt,fin(0));
    for (int i=1;i<=n;i++){
        add(rot[i],rot[i-1],fin(a[i]));
        if (i<LEFTT) continue;
        put(a[i]-query(ck(max(i-RIGHT-1,-1)),rot[max(i-LEFTT,0)],num[i]=1),i);
    }
    long long ans=0;
    for (int i=1;i<=k;i++) ans+=get();
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xu0_zy/article/details/80270519