NOI2010 超级钢琴
题目大意:给定一个长为n的序列。求序列中所有长在\(L~R\)内的价值前K大的连续子序列价值之和。每个子序列的价值是其所有元素之和。
首先我们肯定是要用前缀和相减的方式来计算的。每次取最大的区间,之后只要我们把这个区间删除,继续计算就可以了。
我们有一个很朴素的想法就是枚举左端点,然后用st表求出相对应的最大值的位置和最大值。我们可以先把这些答案都放在一个堆中,取最大,之后删除区间。
怎么删除呢?考虑到删除这个区间,相当于我们能选取的答案区间少了这个节点。那我们只要把这个节点从堆中删除,存入其左右的两个节点即可。注意如果重复了就不要加入。
实现的方法就是开一个结构体维护一下,使用构造函数维护一下就好。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(register int i = a;i <= n;i++)
#define per(i,n,a) for(register int i = n;i >= a;i--)
#define enter putchar('\n')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 500005;
const int N = 10000005;
int read()
{
int ans = 0,op = 1;char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
while(ch >='0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
return ans * op;
}
int n,k,L,R,lg[M],a[M],st[20][M],sum[M];
ll ans;
void init()
{
lg[0] = -1;
rep(i,1,n) st[0][i] = i,lg[i] = lg[i>>1] + 1;
rep(j,1,lg[n])
{
rep(i,1,n)
{
if(i + (1 << j) - 1 > n) break;
int x = st[j-1][i],y = st[j-1][i+(1<<(j-1))];
if(sum[x] > sum[y]) st[j][i] = x;
else st[j][i] = y;
}
}
}
int query(int kl,int kr)
{
int k = lg[kr - kl + 1],x = st[k][kl],y = st[k][kr - (1<<k) + 1];
if(sum[x] > sum[y]) return x;
else return y;
}
struct node
{
int p,l,r,pos;
node(){}
node(int kp,int kl,int kr)
{
p = kp,l = kl,r = kr;
pos = query(kl,kr);
}
bool operator < (const node &g) const
{
return sum[pos] - sum[p-1] < sum[g.pos] - sum[g.p-1];
}
}b[M];
priority_queue <node> q;
int main()
{
n = read(),k = read(),L = read(),R = read();
rep(i,1,n) a[i] = read(),sum[i] = sum[i-1] + a[i];
init();
rep(i,1,n) if(i+L-1 <= n)q.push(node(i,i+L-1,min(i+R-1,n)));
while(k--)
{
node c = q.top();q.pop();
ans += sum[c.pos] - sum[c.p-1];
if(c.pos != c.l) q.push(node(c.p,c.l,c.pos-1));
if(c.pos != c.r) q.push(node(c.p,c.pos+1,c.r));
}
printf("%lld\n",ans);
return 0;
}
当你意识到,每个上一秒都成为永恒。