题目
题目描述
食堂有
N
N
N 种食材,编号从
1
1
1 到
N
N
N ,每种食材有自己的美味度
D
i
D_i
Di 。食堂的厨师很奇怪,他只会用编号连续的食材做菜,并且一道菜包含的食材种类数至少是
L
L
L 种,但又不能超过
R
R
R 种。一道菜的美味度等于它包含的所有种类食材的美味度之和。如果两道菜包含的食材种类完全一致,那么就被认为是相同的。
现在厨师要准备 M M M 道不同的菜,你能告诉他这 M M M 道菜的美味度之和最大是多少吗?
输入输出格式
(见标程)
数据范围与约定
1
≤
L
≤
R
≤
N
≤
500000
,
1
≤
M
≤
500000
,
∣
D
i
∣
≤
1000
1\le L\le R\le N\le 500000,1\le M\le 500000,|D_i|\le 1000
1≤L≤R≤N≤500000,1≤M≤500000,∣Di∣≤1000 。
思路
subtask 1 \text{subtask}1 subtask1:暴力枚举每一个,放优先队列里。 O ( → ∞ ) \mathcal O(\rightarrow \infty) O(→∞) 。
subtask ∞ \text{subtask}\infty subtask∞:
上面的方法,为什么慢呢?或者说,为什么可以改进呢?
显然,有一些方案之间的比较是可以加速的!就是说,有一组 n n n 个方案,全部塞队列来排序是 O ( n log n ) \mathcal O(n\log n) O(nlogn) ,但是有奇技淫巧可以加速成 O ( log n ) \mathcal O(\log n) O(logn)(只求最值,不全部排序)!
记 d x = ∑ i = 1 x D x d_x=\sum_{i=1}^{x}D_x dx=∑i=1xDx ,考虑一群右端点相同的方案,其美味值都等于 d r − d l − 1 d_r-d_{l-1} dr−dl−1 ,由于 d r d_r dr 相同,所以 d l − 1 d_{l-1} dl−1 最小则美味值最大。用个 R M Q \mathbb{RMQ} RMQ 就好了!
更具体的,我们用 ( l , r , R ) (l,r,R) (l,r,R) 表示左端点 L ∈ [ l , r ] L\in[l,r] L∈[l,r] ,右端点就是 R R R ,最大的美味值。把这个玩意儿塞到队列里就行了!找到一个 [ x , R ] [x,R] [x,R] 作为一道菜, ( l , r , R ) (l,r,R) (l,r,R) 就分裂成 ( l , x − 1 , R ) (l,x-1,R) (l,x−1,R) 与 ( x + 1 , r , R ) (x+1,r,R) (x+1,r,R) 。
每次分裂会导致堆里多一个元素,所以一共 m m m 次删除、 2 m + n 2m+n 2m+n 次插入,堆的复杂度是 O ( ( n + m ) log ( n + m ) ) \mathcal O((n+m)\log (n+m)) O((n+m)log(n+m)) ;预处理 R M Q \Bbb{RMQ} RMQ 是 O ( n log n ) \mathcal O(n\log n) O(nlogn) 的。总复杂度是 O [ ( n + m ) log ( n + m ) ] \mathcal O[(n+m)\log(n+m)] O[(n+m)log(n+m)] 。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
inline int readint(){
int a=0,f=1;char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while('0'<=c&&c<='9'){
a=a*10+c-'0';
c=getchar();
}
return a*f;
}
inline void write_unsigned(unsigned long long x){
if(x>9) write_unsigned(x/10);
putchar(x-x/10*10+'0');
}
inline void writeint(long long x){
if(x<0) putchar('-'),x=-x;
return write_unsigned(x);
}
const int MAXN=500000;
int least, most, n, m, d[MAXN+2]; // 前缀和
int RMQ[MAXN+2][20];
void prepareRMQ(){
for(int i=0; i<=n; ++i)
RMQ[i][0] = i;
for(int j=1; (1<<j)<=n+1; ++j)
for(int i=0; i+(1<<j)<=n+1; ++i)
if(d[RMQ[i][j-1]] < d[RMQ[i+(1<<(j-1))][j-1]])
RMQ[i][j] = RMQ[i][j-1];
else
RMQ[i][j] = RMQ[i+(1<<(j-1))][j-1];
}
int QueryRMQ(int l,int r){ // 找d值最小的一个
int j = int(log2(r-l+1));
if(d[RMQ[l][j]] < d[RMQ[r-(1<<j)+1][j]])
return RMQ[l][j];
else
return RMQ[r-(1<<j)+1][j];
}
struct Status{
int r, rangel, ranger, maxD, maxl;
Status(int R,int RL,int RR):r(R),rangel(RL),ranger(RR)
{
maxl = Query_RMQ(rangel,ranger);
maxD = d[r]-d[maxl];
}
bool operator<(const Status &that)const{
return maxD < that.maxD; // 该区间中最大值
}
};
priority_queue<Status> pq;
void work()
{
prepareRMQ();
for(int i=least; i<=n; ++i)
pq.push(Status(i,max(i-most,0),i-least));
long long ans = 0;
while(m --){
Status t = pq.top();
pq.pop(), ans += t.maxD;
if(t.rangel != t.maxl)
pq.push(Status(t.r,t.rangel,t.maxl-1));
if(t.ranger != t.maxl)
pq.push(Status(t.r,t.maxl+1,t.ranger));
}
writeint(ans);
}
int main()
{
n = readint(), m = readint(),
least = readint(), most = readint();
for(int i=1; i<=n; ++i)
d[i] = readint()+d[i-1];
work();
return 0;
}