NOI2010 超级钢琴
作者:互联网
题目链接:戳我
就是我们考虑记录一个三元组qwq,一个是pos,一个是l,一个是r。
我们可以用ST表来查询固定左端点,右端点在一段区间内的最大值所在的位置。
然后我们使用优先队列,不断累加最大值,然后弹出,求它的区间的子区间内的最大值。
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define MAXN 500010
using namespace std;
int n,l,r,k,cnt;
long long ans;
int log2[MAXN],st[MAXN][21],sum[MAXN],a[MAXN];
struct Node{
int st,l,r,maxpos;
friend bool operator < (struct Node x,struct Node y)
{return (sum[x.maxpos]-sum[x.st-1])<(sum[y.maxpos]-sum[y.st-1]);}
};
priority_queue<Node>q;
inline void init()
{
for(int i=1;i<=20;i++)
{
for(int j=1;j+(1<<i)-1<=n;j++)
{
int cur1=st[j][i-1];
int cur2=st[j+(1<<(i-1))][i-1];
if(sum[cur1]>sum[cur2]) st[j][i]=cur1;
else st[j][i]=cur2;
}
}
}
inline int query(int l,int r)
{
if(l==r) return l;
int cur1=st[l][log2[r-l+1]];
int cur2=st[r-(1<<log2[r-l+1])+1][log2[r-l+1]];
if(sum[cur1]>sum[cur2]) return cur1;
else return cur2;
}
inline void solve()
{
for(int i=1;i<=n;i++)
{
int ll=i+l-1,rr=min(n,i+r-1);
if(ll>n) continue;
q.push((Node){i,ll,rr,query(ll,rr)});
// printf("%d %d %d %d\n",i,ll,rr,q.top().maxpos);
}
while(!q.empty()&&cnt<k)
{
Node u=q.top(); q.pop();
// printf("maxpos=%d\n",u.maxpos);
ans+=sum[u.maxpos]-sum[u.st-1];
cnt++;
if(u.maxpos-1>=u.l) q.push((Node){u.st,u.l,u.maxpos-1,query(u.l,u.maxpos-1)});
if(u.maxpos+1<=u.r) q.push((Node){u.st,u.maxpos+1,u.r,query(u.maxpos+1,u.r)});
}
printf("%lld\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d%d%d",&n,&k,&l,&r);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) st[i][0]=i;
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
// for(int i=1;i<=n;i++) printf("sum[%d]=%d\n",i,sum[i]);
log2[0]=-1;
for(int i=1;i<=n;i++) log2[i]=log2[i>>1]+1;
// for(int i=1;i<=n;i++) printf("log2[%d]=%d\n",i,log2[i]);
init();
solve();
return 0;
}
标签:Node,cur2,int,超级,st,钢琴,NOI2010,maxpos,include 来源: https://www.cnblogs.com/fengxunling/p/10672664.html