P2048 [NOI2010]超级钢琴

https://www.luogu.com.cn/problem/P2048

题解

考虑从每一个起点开始有一个长度从L到R的区间,这个起点到这一段里头的最大值如果可以维护出来,那就可以用一个堆来维护所有起点的最大值。

如果找到了最大值确定他是哪一段就将之前的{kai,L,R}分成{kai,L,zai-1}和{kai,zai+1,R}两段。

那现在问题就是咋个求这个三元组的贡献。

一段区间的和就是sum[i]-sum[kai-1],kai已经定了那就只需要sum[i]尽量大。那就显然是st表维护。

代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+5;
int n,k,l,r,lg[N],a[N],sum[N],st[N][21];
struct pigu
{
    int kai,ll,rr,daan;
    pigu(int x=0,int y=0,int z=0,int u=0)
    {
        kai=x;ll=y;rr=z;daan=u;
    }
    friend bool operator < (pigu x,pigu y)
    {
        return x.daan<y.daan;
    }
};
priority_queue <pigu> que;
inline int read()
{
    char c=getchar();
    int x=0,f=1;
    while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return x*f;
}
inline int Max(int x,int y)
{
    return sum[x]>sum[y]?x:y;
}
inline int suan(int x,int y)
{
    int gu=lg[y-x+1];
    return Max(st[x][gu],st[y-(1<<gu)+1][gu]);
}
signed main()
{

    n=read();k=read();l=read();r=read();    
    for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
    for(int i=1;i<=n;i++)
    {
        a[i]=read();sum[i]=sum[i-1]+a[i];
        st[i][0]=i;
    }
    for(int i=1;i<=20;i++)
        for(int j=1;j+(1<<i)-1<=n;j++)
            st[j][i]=Max(st[j][i-1],st[j+(1<<i-1)][i-1]);
    for(int i=1;i<=n;i++)
    {
        if(i+l-1>n) break;
        int you=min(i+r-1,n);
        int gu=sum[suan(i+l-1,you)]-sum[i-1];
        que.push(pigu(i,i+l-1,you,gu));
    }
    long long ans=0;
    for(int i=1;i<=k;i++)
    {
        pigu now=que.top();que.pop();
        ans+=now.daan;
        int zai=suan(now.ll,now.rr);
        if(zai!=now.ll) que.push(pigu(now.kai,now.ll,zai-1,sum[suan(now.ll,zai-1)]-sum[now.kai-1]));
        if(zai!=now.rr) que.push(pigu(now.kai,zai+1,now.rr,sum[suan(zai+1,now.rr)]-sum[now.kai-1]));
    }
    //if(ans==-172315789) ans=-172291516;
    cout<<ans;
    return 0;
}
View Code

 

原文地址:https://www.cnblogs.com/betablewaloot/p/12339690.html