【bzoj2006】【NOI2010】【超级钢琴】

2006: [NOI2010]超级钢琴

Time Limit: 20 Sec Memory Limit: 552 MB
Submit: 1778 Solved: 871
[Submit][Status][Discuss]
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,表示按编号从小到大每个音符的美妙度。
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。

【bzoj2006】【NOI2010】【超级钢琴】_第1张图片

我们处理出这样一个东西(i,L,R)表示当这个和弦的左端点为i时在上限和下限中的最优值。(也就是右端点在[i+l-1,i+r-1]中)
我们将第一步处理出来的所有最优值扔到一个堆里面,然后每次从堆中选取最大的出来,将ans加上这个数。假设我们在这段区间中选取最优值得位置为where,那么我们就这段区间[L,R]裂解成两段区间[L,where-1]和[where+1,R],再讲这两段区间放到堆里面。
这样其实就是每次选取了一个最优值,然后把那些小于最优值的数放进去。
选取了K次之后就是答案。

我们对于第一步的处理,其实有两种方法:
首先我们都需要处理出前缀和这个东西来,然后就是查询区间最值,查完后再加上i到L的值就好了。对于查区间最值:
①:我们可以用线段树。但是我们的查询次数是(n+k),所以会比较慢。
②:我们可以用ST(一种跟倍增差不多的东西),用ST就可以做到O(1)的查询,会快很多

然后堆就直接STL把,手写比较麻烦
(由于我比较弱,想了一下午才想出怎么做。。。)

线段树+priority_queue

#include
#include
#include
#include
using namespace std;
#define inf 210000000LL
const int N=500100;
int n,K,a[N];
long long ans=0;
struct S{
    int where,maxn;
    void init(){
        maxn=-inf;
        where=0;
    }
}tr[N*4];
struct heap{int maxn,where,left,llimit,rlimit;};
priority_queue q;
bool operator < (heap x,heap y){return x.maxnint in()
{
    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;
}
#define mid (l+r)/2
#define L k<<1,l,mid
#define R k<<1|1,mid+1,r
void build(int k,int l,int r)
{
    if(l==r){
        tr[k].maxn=a[l];
        tr[k].where=l;
        return ;
    }
    build(L); build(R);
    tr[k].maxn=max(tr[k<<1].maxn,tr[k<<1|1].maxn);
    if(tr[k<<1].maxn>tr[k<<1|1].maxn) tr[k].where=tr[k<<1].where;
    if(tr[k<<1].maxn<=tr[k<<1|1].maxn) tr[k].where=tr[k<<1|1].where;
}
S ask(int k,int l,int r,int x,int y)
{
    S ans1,ans2;
    ans1.init();ans2.init();
    if(x<=l&&y>=r) return tr[k];
    if(x<=mid) ans1=ask(L,x,y);
    if(y>mid) ans2=ask(R,x,y);
    if(ans1.maxnwhere=ans2.where;
    }
    return ans1;
}
int main()
{
    int i,j,l,r,ll,rr;
    S t;
    heap ans1;
    n=in();K=in();l=in();r=in();
    for(i=1;i<=n;++i) a[i]=in(),a[i]+=a[i-1];
    build(1,1,n);   
    for(i=1;i<=n-l+1;++i){
        ll=i+l-1,rr=min(i+r-1,n);
        t=ask(1,1,n,ll,rr);
        q.push((heap){t.maxn-a[i-1],t.where,i,ll,rr});
    }
    while(K--){
        ans1=q.top();
        q.pop();
        ans+=(long long)ans1.maxn;
        if(ans1.where-1>=ans1.llimit){
            t=ask(1,1,n,ans1.llimit,ans1.where-1);
            q.push((heap){t.maxn-a[ans1.left-1],t.where,ans1.left,ans1.llimit,ans1.where-1});
        }
        if(ans1.where+1<=ans1.rlimit){
            t=ask(1,1,n,ans1.where+1,ans1.rlimit);
            q.push((heap){t.maxn-a[ans1.left-1],t.where,ans1.left,ans1.where+1,ans1.rlimit});
        }
    }
    printf("%lld\n",ans);
}

ST+priority_queue

#include
#include
#include
#include
#include 
using namespace std;
#define inf 210000000LL
const int N=500100;
int n,K,a[N];
long long ans=0;
struct S{int maxn,where;}f[20][N];
struct heap{int maxn,where,left,llimit,rlimit;};
priority_queue<heap> q;
bool operator < (heap x,heap y){return x.maxn<y.maxn;}
int in()
{
    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;
}
void prepare()
{
    int i,j;
    for(i=1;i<=20;++i)
      for(j=1;j+(1<
        f[i][j].maxn=max(f[i-1][j].maxn,f[i-1][j+(1<>1)].maxn);
        if(f[i-1][j].maxn>f[i-1][j+(1<>1)].maxn) f[i][j].where=f[i-1][j].where;
        else f[i][j].where=f[i-1][j+(1<>1)].where;
      }
}
S ask(int x,int y)
{
  S ans1;
  int t=(int)(log(y-x+1.0)/log(2.0));
  ans1.maxn=max(f[t][x].maxn,f[t][y-(1<].maxn);
  if (f[t][x].maxn>f[t][y-(1<].maxn) ans1.where=f[t][x].where;
  else ans1.where=f[t][y-(1<].where;
  return ans1;
}
int main()
{
    int i,j,l,r,ll,rr;
    S t;
    heap ans1;
    n=in();K=in();l=in();r=in();
    for(i=1;i<=n;++i){
        a[i]=in();
        a[i]+=a[i-1];
        f[0][i].maxn=a[i];
        f[0][i].where=i;
    } 
    prepare();
    for(i=1;i<=n-l+1;++i){
        ll=i+l-1,rr=min(i+r-1,n);
        t=ask(ll,rr);
        q.push((heap){t.maxn-a[i-1],t.where,i,ll,rr});
    }
    while(K--){
        ans1=q.top();
        q.pop();
        ans+=(long long)ans1.maxn;
        if(ans1.where-1>=ans1.llimit){
            t=ask(ans1.llimit,ans1.where-1);
            q.push((heap){t.maxn-a[ans1.left-1],t.where,ans1.left,ans1.llimit,ans1.where-1});
        }
        if(ans1.where+1<=ans1.rlimit){
            t=ask(ans1.where+1,ans1.rlimit);
            q.push((heap){t.maxn-a[ans1.left-1],t.where,ans1.left,ans1.where+1,ans1.rlimit});
        }
    }
    printf("%lld\n",ans);
}

你可能感兴趣的:(线段树,堆)