bzoj2122: 工作评估 分块 二分

bzoj2122: 工作评估

Description

利用空闲时间,BX希望外出工作,工作开始之前,公司就会给BX一个评估值X0,之后每天BX的评估值都是根据上一
天的评估值和当天公司的运行状况得出,即Xi=Xi-1+Di,但是每天的评估值有一个上限,也就是说完整的评估公式
因该是Xi=min{Xi-1+Di,Li}。现在BX已经知道了该公司对自己的初始评估值X0、公司每天的运行状况Di、每天的评
估上限Li,他的空闲时间是从第A天到第B天,他希望找到一段时间i,j (A≤i≤j≤B),使得从第i天开始工作,到
第j天结束后的评估值最大。当然如果任意一段时间的工作得到评估值都<初始评估值X0,BX可以选择不工作,从而
得到最大的评估值。

Input

输入的第一行包含两个整数N、M,分别表示总共工作天数和询问数。
第二行N个数,表示Di。第三行N个数,表示Li。
以下M行,每行3个数A、B、X0,表示一次询问。

Output

M行,每行输出一个整数,表示评估的最大值

Sample Input

6 3
-6 5 3 2 -3 4
8 10 8 1 9 9
1 3 9
2 6 3
3 4 0

Sample Output

10
8
3
【数据规模】
对于100%数据,满足N,M<50001,|Di|<10001,0≤Li<1000000001

分析

分块好题。
首先需要知道两个结论,设 f ( l , r , x 0 ) f(l,r,x_0) f(l,r,x0)表示以 x 0 x_0 x0为初始评估值从 l l l走到 r r r的最终评估值。

结论1:若 a ≥ b a\ge b ab,则 f ( l , r , a ) ≥ f ( l , r , b ) f(l,r,a) \ge f(l,r,b) f(l,r,a)f(l,r,b)
结论2:设 G ( l , r ) = f ( l , r , i n f ) , S ( l , r ) = ∑ i = l r d i G(l,r)=f(l,r,inf),S(l,r)=\sum_{i=l}^{r} d_i G(l,r)=f(l,r,inf),S(l,r)=i=lrdi f ( l , r , x 0 ) = min ⁡ { G ( l , r ) , S ( l , r ) + x 0 } f (l,r,x_0)=\min\{G(l,r), S(l,r)+x_0\} f(l,r,x0)=min{G(l,r),S(l,r)+x0}

结论1是显然的,对于结论2,如果在某一次被限制 l i l_i li卡住了,那么答案显然就是 G ( l , r ) G(l,r) G(l,r),否则的话如果一次都没有被卡住,答案就是 S ( l , r ) + x 0 S(l,r)+x_0 S(l,r)+x0,再由结论1可得, G ( l , r ) ≥ S ( l , r ) + x 0 G(l,r)\ge S(l,r)+x_0 G(l,r)S(l,r)+x0(没有卡住),所以答案直接取 min ⁡ \min min

考虑分块,采用块内快外分开计算贡献的方法。

块内

如果我们已经处理除了任意一对 ( l , r ) (l,r) (l,r) G ( l , r ) , S ( l , r ) G(l,r),S(l,r) G(l,r),S(l,r),考虑怎么快速求出答案。
一个显然的事情是,如果 G ( l , r ) ≥ G ( l 1 , r 1 ) ∧ S ( l , r ) ≥ S ( l 1 , r 1 ) G(l,r)\ge G(l_1,r_1)\land S(l,r)\ge S(l_1,r_1) G(l,r)G(l1,r1)S(l,r)S(l1,r1),那么可以直接舍去 l 1 , r 1 l_1,r_1 l1,r1
这样我们得到了一个 G i G_i Gi递增, S i S_i Si递减的序列,希望求出 max ⁡ i min ⁡ { G i , S i + x 0 } \max_i\min \{G_i,S_i+x_0\} maximin{Gi,Si+x0}
显然可以二分求最大值。

块外

不完整块可以暴力碾过去。
对于一个完整块,有几种选项。

1.从本块开始,从本块结束
2.从本块开始,走到块尾
3.从上一块走过来,从本块结束
4.从上一块走过来,走到块尾
5.不走这一块

于是预处理块内答案,每个位置走到块尾,从块头走到每个位置的 G ( l , r ) , S ( l , r ) G(l,r),S(l,r) G(l,r),S(l,r),用二分的方法求 1 , 2 , 3 1,2,3 1,2,3的答案, 4 4 4直接走过去, 5 5 5用开始的答案更新即可。

代码

#include<bits/stdc++.h>
const int M = 250, N = 5e4 + 5;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
struct O {
	int g, s;
	O(int _g = 0, int _s = 0) : g(_g), s(_s) {}
}o[N], v[M][N], pr[M][M], sf[M][M];
bool cmp(O a, O b) {return a.g == b.g ? a.s < b.s : a.g < b.g;}
int to, tp, st[N], b[N], l[N], r[N], d[N], li[N], s[N], f[M][M][M];
int S(int l, int r) {return s[r] - s[l - 1];}
int G(int L, int R) {return f[b[L]][L - l[b[L]]][R - l[b[L]]];}
int cn(int a, int b) {return a < b ? a : b;}
int cm(int a, int b) {return a > b ? a : b;}
void Up(int &a, int b) {a < b ? a = b : 0;}
void Cal(O *f) {
	std::sort(o + 1, o + to + 1, cmp);
	tp = 0;
	for(int i = 1;i <= to; ++i) {
		for(;tp && o[i].s >= o[st[tp]].s;) --tp;
		st[++tp] = i;
	}
	f[0].s = tp; for(int i = 1;i <= tp; ++i) f[i] = o[st[i]];
}
void Pre(int x) {
	for(int L = l[x]; L <= r[x]; ++L) {
		int st = 0x3f3f3f3f;
		for(int R = L; R <= r[x]; ++R)
			st = cn(st + d[R], li[R]), f[x][L - l[x]][R - l[x]] = st;
	}
	to = 0;
	for(int L = l[x]; L <= r[x]; ++L)
		for(int R = L; R <= r[x]; ++R)
			o[++to] = O(G(L, R), S(L, R));
	Cal(v[x]);
	to = 0;
	for(int R = l[x]; R <= r[x]; ++R) 
		o[++to] = O(G(l[x], R), S(l[x], R));
	Cal(pr[x]);
	to = 0;
	for(int L = l[x]; L <= r[x]; ++L)
		o[++to] = O(G(L, r[x]), S(L, r[x]));
	Cal(sf[x]);
}
O Move(int L, int R, int cr, int st) {
	int r = cr;
	for(int u = L; u <= R; ++u) 
		cr = cn(cm(cr, st) + d[u], li[u]), Up(r, cr);
	return O(cr, r);
}
int Go(O *f, int st) {
	int L = 1, R = f[0].s;
	for(;L + 1 < R; ) { 
		int m = L + R >> 1; 
		f[m].g > f[m].s + st ? R = m : L = m;
	} 
	int r = cn(f[L].g, f[L].s + st); ++L;
	if(L <= f[0].s) Up(r, cn(f[L].g, f[L].s + st));
	return r;
}
int Work(int L, int R, int st) {
	if(b[L] == b[R]) return Move(L, R, st, st).s;
	O x = Move(L, r[b[L]], st, st); int cr = cm(x.g, st), A = x.s;
	for(int u = b[L] + 1; u < b[R]; ++u) {
		Up(A, Go(pr[u], cr)); //上一块接着走,在这一块停下。 
		Up(A, Go(v[u], st)); //这一块重新走,在这一块停下。	
		cr = cn(G(l[u], r[u]), S(l[u], r[u]) + cr); //暴力走过这一块。
		Up(cr, Go(sf[u], st));//这一块重新走,不停下
		Up(cr, st); //不走这一块 
	}
	return cm(A, Move(l[b[R]], R, cr, st).s);
}
int main() {
	int n = ri(), m = ri(), B = sqrt(n);
	for(int i = 1;i <= n; ++i) s[i] = s[i - 1] + (d[i] = ri());
	for(int i = 1;i <= n; ++i) li[i] = ri();
	for(int i = 1;i <= n; ++i) {
		b[i] = (i - 1) / B + 1; r[b[i]] = i;
		!l[b[i]] ? l[b[i]] = i : 0; 
	}
	for(int i = 1;i <= b[n]; ++i) Pre(i);
	for(;m--;) {
		int L = ri(), R = ri(), st = ri();
		printf("%d\n", Work(L, R, st));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值