[usOJ2849]奇怪的厨师

137 篇文章 1 订阅
89 篇文章 0 订阅

题目

传送门 to usOJ

题目描述
食堂有 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 1LRN5000001M500000Di1000

思路

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} drdl1 ,由于 d r d_r dr 相同,所以 d l − 1 d_{l-1} dl1 最小则美味值最大。用个 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,x1,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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值