单调队列.
Table of Contents
- Description
- Solution
Description
给定一个长度为$n$的序列,你有一次机会选中一段连续的长度不超过$d$的区间,将里面所有数字全部修改为$0$。
请找到最长的一段连续区间,使得该区间内所有数字之和不超过$p$。
Solution
首先, 我们要改就一定改掉完全一段长为$d$的区间.
然后我们对这个区间和跑单调队列, 这时我们得到了这个位置以及之前的区间和最大值. 不难发现随着$i$右移, 可行的左端点一定会随之右移, 这个过程可以跟随单调队列跑, 注意每次删去不合法的队头(也就是队头删去$d$个位置后小于左端点). 我们不断右移左端点, 直到区间和小于等于$p$, 同时队头也跟随着移动. 最后更新答案.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cctype>
using namespace std;
const int maxn = 2e+6 + 5;
typedef long long ll;
int n, d; ll a[maxn], p, hd, tl, sum[maxn], inv[maxn]; int que[maxn];
inline int rd() { register int x = 0; char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)) x = x * 10 + (c ^ 48), c = getchar(); return x; } inline ll rdll() { register ll x = 0; char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)) x = x * 10 + (c ^ 48), c = getchar(); return x; }
int main() { n = rd(); p = rdll(); d = rd(); for(int i = 1; i <= n; ++i) a[i] = rdll(), sum[i] = sum[i - 1] + a[i]; for(int i = d; i <= n; ++i) inv[i] = sum[i] - sum[i - d]; int len = d, lft = 1; hd = 0; tl = 1; que[0] = d; for(int i = d + 1; i <= n; ++i) { while(hd < tl && inv[i] > inv[que[tl - 1]]) --tl; que[tl++] = i; while(hd < tl && que[hd] - d + 1 < lft) hd++; while(hd < tl && sum[i] - sum[lft - 1] - inv[que[hd]] > p) { ++lft; while(hd < tl && que[hd] - d + 1 < lft) hd++; } len = max(len, i - lft + 1); } printf("%d\n", len); return 0; }
|