题解:P8102 「LCOI2022」 Cow Insertion
很有意思的题目喵。
题意
注:本题解中用 \(a_0\) 表示原题干中的 \(A\)。
给定由 \(n\) 个自然数组成的序列 \(a_1,a_2,\dots,a_n\) 和一个自然数 \(a_0\),将 \(a_0\) 插入序列 \(a\) 后用一个长为 \(m\) 的滑动窗口,每次记录 \(m\) 个数的最大值。
最大化上述操作中的最大值之和。
思路
首先考虑如果没有增加一个 \(a_0\) 的条件怎么做:那不就是单调队列板子了吗?
然后回到原来的题意:
考虑增加一个 \(a_0\) 的本质:假设将 \(a_0\) 加在了 \(a_i,a_{i+1}\) 之间,对原来的很多段的贡献都是毫无影响的,例如下图(数字代表的是下标而不是值,\(0\) 代表 \(a_0\) 的位置):

显然这两个区间是不受影响的。
下面考虑被影响的区间:
-
增加的区间:

显而易见,这三个区间都包含加入的新元素,是之前未曾存在的。观察发现这三个区间,都是 \(m-1\) 个旧元素与 \(a_0\) 组成的。这些段的共性是包含 \(a_0\)。
-
减少的区间:

在加入新元素后的序列中这两段不复存在了。因为新加入的元素使他们不在连续。不难发现,“消失”的段应该满足的条件是同时包含 \(a_i\) 和 \(a_{i+1}\)。
综上所述:插入一个数对原来序列的影响就是增加了 \(m\) 个新区间的贡献,又减少了 \(m-1\) 个原区间的贡献。
因此思路油然而生了:先预处理每一个长度为 \(m\) 段里的最大值,再预处理每一个长度为 \(m-1\) 段与 \(a_0\) 里的最大值。
想到要对这些区间最大值进行相加,因此可以继续预处理这两个预处理值的前缀和。
假设右端点从 \(1\) 到 \(i\) 中,长度为 \(m\) 段里的最大值之和为 \(sum_{1,i}\),长度为 \(m-1\) 段与 \(a_0\) 里的最大值之和为 \(sum_{2,i}\)。这里用右端点是为了便于进行单调队列操作,\(sum_1\) 是从 \(m\) 处才开始有而 \(sum_2\) 是从 \(m - 1\) 处才开始有的。
那么,对于将 \(a_0\) 插入到 \(a_i\) 后面的位置,答案就很好求出来了:
整理一下就是:
时间复杂度 \(\Theta (n)\),足以通过此题。
代码
那把这个公式一代入不就写完了吗(只不过显得有点长罢了)。
注意事项:
-
那如果下标超过了 \(n\) 我们可以将其视为一直到序列末端,也就是把超过 \(n\) 的都视为 \(n\)。
-
对应地,如果 \(sum_1\) 下标不足 \(m\),或 \(sum_2\) 下标不足 \(m - 1\),则补足为 \(m\) 或 \(m - 1\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define loop(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define rloop(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
const int N=5e6+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int n,m,a0,a[N];
int q[N],l,r;
ll sum1[N],sum2[N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>a0;
loop(i,1,n)cin>>a[i];
if(m==1){ // 特判
ll ans=a0;
loop(i,1,n)ans+=a[i];
cout<<ans<<'\n';
return 0;
}
// 预处理长度为 m 的段
l=1,r=0;
loop(i,1,n){
while(l<=r&&a[q[r]]<=a[i])r--;
while(l<=r&&q[l]+m<=i)l++;
q[++r]=i;
if(i>=m)sum1[i]=sum1[i-1]+a[q[l]];
}
// 预处理长度为 m-1 的段
l=1,r=0;
loop(i,1,n){
while(l<=r&&a[q[r]]<=a[i])r--;
while(l<=r&&q[l]+m-1<=i)l++;
q[++r]=i;
if(i>=m-1)sum2[i]=sum2[i-1]+max(a[q[l]],a0);
}
// 求答案
ll ans=sum1[n]+sum2[m-1];
loop(i,m,n){
ans=max(ans,sum1[n]-(sum1[min(n,i+m-1)]-sum1[max(m-1,i)])+(sum2[min(n,i+m-1)]-sum2[max(m-2,i-1)]));
}
cout<<ans<<'\n';
return 0;
}
后记
感谢 @qixiandaa 在代码查错上给了我莫大的帮助喵~
完结撒花花!~
本文来自博客园,作者:Circle_Table,转载请注明原文链接:https://www.cnblogs.com/Circle-Table/articles/19836441

浙公网安备 33010602011771号