您的位置:首页 > 其它

BZOJ 2006: [NOI2010]超级钢琴

2017-08-29 21:02 337 查看
Orz zzk

最直接的想法是找出所有不同的长度在[L,R]的子段然后选最大的k个加到答案中

但是太暴力没前途啊

zzk教会我:

首先构造前缀和数组pre

定义一个三元组MAX(i,L,R)表示以i为右端点且左端点在[L,R]之间的使pre[i]-pre[t-1]最大的值

(等价于[L,R]中使pre[t-1]最小的值) 其中(L<=t<=R)

显然ans就是k个最大的这样的三元组的值的和

可以用ST表+堆

首先枚举i,用ST表找到最小的pre[t] (作为初始状态) 放入一个堆中

然后进行k次查找:每次找到堆顶计入ans,然后因为需要选出的子段各不相同,把(i,L,R)拆成(i,L,t-1)和(i,t+1,r)放入堆中

#include<cstdio>
#include<algorithm>
#include<queue>
#define N 500005
#define RG register
#define LL long long
#define maxlog 20
using namespace std;

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

int n,L,R,k;
LL ans;
int pre
,Log[N+5],ST
[maxlog+5];
struct node
{
int i,l,r,t;
node(int a,int b,int c,int d) {i=a;l=b;r=c;t=d;}
bool operator < (const node &c) const
{
return pre[i]-pre[t-1]<pre[c.i]-pre[c.t-1];
}
};

int MIN(int a,int b){return pre[a-1]<pre[b-1]?a:b;}

int query(int x,int y)
{
int p=Log[y-x+1];
return MIN(ST[x][p],ST[y-(1<<p)+1][p]);
}

priority_queue<node> heap;

int main()
{
n=read(),k=read(),L=read(),R=read();
RG int i,j;
for(i=1;i<=n;++i) pre[i]=read()+pre[i-1],ST[i][0]=i;
for(i=2;i<=n;++i) Log[i]=Log[i>>1]+1;
for(j=1;(1<<j)<=n;++j)
for(i=1;i<=n-(1<<j)+1;++i)
ST[i][j]=MIN(ST[i][j-1],ST[i+(1<<j-1)][j-1]);
for(i=L;i<=n;++i)
{
int l=max(i-R+1,1),r=i-L+1;
heap.push(node(i,l,r,query(l,r)));
}
ans=0;
for(i=1;i<=k;++i)
{
node x=heap.top();heap.pop();
ans+=1ll*(pre[x.i]-pre[x.t-1]);
if(x.t>x.l) heap.push(node(x.i,x.l,x.t-1,query(x.l,x.t-1)));
if(x.t<x.r) heap.push(node(x.i,x.t+1,x.r,query(x.t+1,x.r)));
}
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: