【BZOJ4385】[POI2015]Wilcze do?y【单调队列】【前缀和】【Two Pointers】

【题目链接】

题解:

如果区间[j, i]固定,那么一定是将权值最大的一段变为0。

用单调队列维护一段区间内权值最大的子段下标(这里记录右端点下标,设为x),枚举右端点i,用尺取法计算出j。

一段区间[j, i]合法的条件是sum[i] - sum[j - 1] - (sum[x] - sum[x - d]) <= p。

复杂度:

时间复杂度:因为每个点最多遍历2次,复杂度为O(n)。

空间复杂度:O(n)

RE:

更新j的时候忘写h <= t了。如果写对拍的话应该可以拍出来。

GET:

如果d = 0,那么就是最裸的尺取法。

从尺取法出发,想了以下几种方法,但都可以证明是错的:

1 先找最长区间,满足区间和不大于p,然后将权值最大的一段变为0,再扩展。

反例:n = 9,p = 3,d = 1,10 1 1 1 10 1 2 1 1,答案应该为4。

2 找到权值最大的长度为d的区间,变为0,然后再从这里开始扩展。

反例:n = 8,p = 19,d = 2,10 11 12 10 1 1 1 1,答案应该为7。

所以不能静态维护了,考虑加入一个数,然后就想到了标解,但是不知道如何维护一段区间内固定长度权值最大的子段值。

应该用单调队列+前缀和。

又被单调队列+前缀和卡住了,看来得总结一下。

/* Telekinetic Forest Guard */
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int maxn = 2000005;

int n, d;
LL p, sum[maxn];
int q[maxn];

template <class numtype>
inline void read(numtype &x) {
	bool f = 0; x = 0; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? 1 : 0;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}

int main() {
	read(n); read(p); read(d);
	for(int i = 1; i <= n; i++) read(sum[i]), sum[i] += sum[i - 1];

	int h = 1, t = 0, ans = 0;
	for(int i = d, j = 1; i <= n; i++) {
		for(; h <= t && sum[i] - sum[i - d] > sum[q[t]] - sum[q[t] - d]; t--);
		q[++t] = i;
		for(; h <= t && sum[i] - sum[j - 1] - (sum[q[h]] - sum[q[h] - d]) > p; j++)
			if(q[h] - d <= j) h++;
		ans = max(ans, i - j + 1);
	}

	printf("%d\n", ans);
	return 0;
}
时间: 2024-11-06 03:36:58

【BZOJ4385】[POI2015]Wilcze do?y【单调队列】【前缀和】【Two Pointers】的相关文章

BZOJ4385 : [POI2015]Wilcze do?y

求出前缀和$s$,设$f[i]=s[i+d-1]-s[i-1]$. 从左到右枚举的右端点$i$,左端点$j$满足单调性,若$s[i]-s[j-1]-\max(区间内最大的f)\leq p$,则可行. 用单调队列维护即可,时间复杂度$O(n)$. #include<cstdio> #define N 2000010 int n,d,i,j,q[N],h,t,ans;long long p,sum,s[N],f[N]; inline void read(int&a){char c;whil

bzoj 4385: [POI2015]Wilcze do?y

4385: [POI2015]Wilcze do?y Description 给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0.请找到最长的一段连续区间,使得该区间内所有数字之和不超过p. Input 第一行包含三个整数n,p,d(1<=d<=n<=2000000,0<=p<=10^16).第二行包含n个正整数,依次表示序列中每个数w[i](1<=w[i]<=10^9). Output 包含一行一个正整数,即修改后能

bzoj4385 &amp; POJ2015 Wilcze do?y

Description 给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0.请找到最长的一段连续区间,使得该区间内所有数字之和不超过p. Input 第一行包含三个整数n,p,d(1<=d<=n<=2000000,0<=p<=10^16).第二行包含n个正整数,依次表示序列中每个数w[i](1<=w[i]<=10^9). Output 包含一行一个正整数,即修改后能找到的最长的符合条件的区间的长度. Sample In

4385: [POI2015]Wilcze do?y

原题:http://www.lydsy.com/JudgeOnline/problem.php?id=4385 Description 给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0.请找到最长的一段连续区间,使得该区间内所有数字之和不超过p. Input 第一行包含三个整数n,p,d(1<=d<=n<=2000000,0<=p<=10^16).第二行包含n个正整数,依次表示序列中每个数w[i](1<=w[i]<=

@bzoj - [email&#160;protected] [POI2015] Wilcze do?y

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的序列,你有一次机会选中一段连续的长度不超过 d 的区间,将里面所有数字全部修改为 0. 请找到最长的一段连续区间,使得该区间内所有数字之和不超过 p . input 第一行包含三个整数 n, p, d (1 <= d <= n <= 2000000,0 &

NC17070 矩阵(前缀和+单调队列)

最大子矩阵和问题,一般都是用前缀和先计算行,然后枚举行,在列方向做单调队列 这样的复杂度是N^3,对于几百的数据足够了 #include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<map> #include<string> using namespace std; typedef long long ll; const int N

BZOJ_1096_[ZJOI2007]_仓库建设_(斜率优化动态规划+单调队列+特殊的前缀和技巧)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1096 有\(n\)个工厂,给出第\(i\)个工厂的到1号工厂的距离\(x[i]\),货物数量\(p[i]\),建设仓库所需花费\(c[i]\). 现在要把所有货物都装入仓库,第\(i\)号工厂的货物可以选择在\(i\)建仓库并存入,或者移动到\(k\)号仓库\((i<k<=n)\).移动的花费为数量与距离的乘积. 分析 我们来想一想dp方程. 用\(dp[i]\)表示前\(i\)个工厂,且

AcWing:135. 最大子序和(前缀和 + 单调队列)

输入一个长度为n的整数序列,从中找出一段长度不超过m的连续子序列,使得子序列中所有数的和最大. 输入格式 第一行输入两个整数n,m. 第二行输入n个数,代表长度为n的整数序列. 同一行数之间用空格隔开. 输出格式 输出一个整数,代表该序列的最大子序和. 数据范围 1≤n,m≤3000001≤n,m≤300000 输入样例: 6 4 1 -3 5 1 -2 3 输出样例: 7 算法:前缀和 + 单调队列 注意:单调队列需要使用双端队列deque,因为其中需要头部弹出以及尾部弹出. #include

bzoj4385 Wilcze do?y

Description 给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0.请找到最长的一段连续区间,使得该区间内所有数字之和不超过p. Input 第一行包含三个整数n,p,d(1<=d<=n<=2000000,0<=p<=10^16).第二行包含n个正整数,依次表示序列中每个数w[i](1<=w[i]<=10^9). Output 包含一行一个正整数,即修改后能找到的最长的符合条件的区间的长度. 单调队列扫描,记录