【欧少队列】NOIP2012模拟题BY JZP_Day1_Gift

本文介绍了一种使用单调队列与动态规划相结合的方法来解决序列问题,具体涉及从序列中选取无交集子段的问题,要求子段长度大于k且段内最大值与最小值之差不大于m,目标是求最多能选取多少个子段。通过神级做法,直接记录每个点和前k-1个数的最大最小值,并利用动态规划进行求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【注:欧少队列 == 单调队列】


题意:给定一个序列,要求从其中取出一些无交集的子段,使得子段长大于k且段中的最大值最小值之差不大于m,问最多能取出多少个。


单调队列题。考场没写出来。

标准做法是:维护最大值最小值和F[k] - k的单调队列。


XJJ的神级做法:直接记录每个点和前k-1个数的最大最小值,F记两维,f[0][k] 表示必须不取k的1~k的最优值,f[1][k]表示必须取k的1~k最优值。

然后就可以做了..跪。

最近又在写p了。嗯。为了联(bu)赛(luo)。


program gift;
const
	maxn = 300005;
	inf = 'gift.in';
	ouf = 'gift.out';		
var
	i, k, n, m, t, head, tail : longint;
	mx, mn : Array[0 .. maxn] of longint;
	f : Array[0 .. 1, 0 .. maxn] of longint;
	w, q : Array[0 .. maxn] of longint;
	
function min(a, b : longint) : longint; begin if a < b then exit(a) else exit(b); end;
function max(a, b : longint) : longint; begin if a > b then exit(a) else exit(b); end;

begin
	assign(input, inf); reset(input);
	assign(output, ouf); rewrite(output);
	readln(t);
	while t > 0 do begin
		readln(n, m, k);
		fillchar(f, sizeof(f), 0);
		for i := 1 to n do read(w[i]);
		mx[1] := w[1]; q[1] := 1;
		head := 1; tail := 1;
		for i := 2 to n do begin
			while (tail >= head) and (w[i] > w[q[tail]]) do dec(tail);
			inc(tail); q[tail] := i;
			if (i - q[head] >= k) then inc(head);
			mx[i] := w[q[head]];
		end;
		mn[1] := w[1]; q[1] := 1;
		head := 1; tail := 1;
		for i := 2 to n do begin
			while (tail >= head) and (w[i] < w[q[tail]]) do dec(tail);
			inc(tail); q[tail] := i;
			if (i - q[head] >= k) then inc(head);
			mn[i] := w[q[head]];
		end;
		for i := k to n do begin
			f[0, i] := max(f[0, i - 1], f[1, i - 1]);
			if (max(mx[i - 1], w[i]) - min(mn[i - 1], w[i]) <= m) then 
				f[1, i] := f[1, i - 1] + 1 
			else 
				f[1, i] := 0;
			if (mx[i] - mn[i] <= m) then 
				f[1, i] := max(f[1, i], k + max(f[0, i - k], f[1, i - k]));
		end;
		writeln(max(f[1, n], f[0, n]));
		dec(t);
	end;
	close(input); close(output);
end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值