石中集训8.9测试 T4 奶牛
考场上写了 80 分的暴力,但离正解真的很近。只需要用一点初中(小学生也会)的知识正解了。
题意
有一个长度为 的序列 。你可以将 插入在 的任意位置。定义 为插入 的新序列。定义 。求最大的 。
或者可以这样:
很明显根本看不懂。
输入格式
n m x
a1,a2,a3...an
思路
先从暴力讲起。枚举将 插入在 与 。也即枚举 序列。然后可以用单调队列求定长区间最值。复杂度 。
但是这样要求在线,也就是说这样写的复杂度无法改变。
观察每个区间最值,我们可以分成三类。
- 被 影响了的区间。
- 和原来一样的区间。
- 新加入的区间。
举个例子。
3 2 50
60 100 70
假设将 插入在 后。序列变成:
60 50 100 70
- 是被影响的区间;
- 是原本的区间;
- 是新加入的区间;
设 为在 插入 。就可以遍历区间起点 ,判断区间的类型求解。
被影响的区间满足 ,包含原序列中 (最后的数被 替代了)。
原本的区间有两种,在 前与后,分别满足 ,区间仍为 。
新加入的区间分成两类,其一是以 为起点的。包含原序列中的 ( 代替 )。
当然,若不能以 为起点,即 时,区间以 序列元素为起点。该区间包含原序列中的 ,另外,除原序列以外,还包括 。
对于原数列的区间最值,可以用 st 表求解。
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int n,m,a[N],st[N][25],c[N],d[N],s[N],lg[N],cval;
void init() {
for(int i=2;i<=n;i++) {
lg[i]=lg[i>>1]+1;
}
for(int j=1;j<=24;j++) {
for(int i=1;i+(1<<j)-1<=n;i++) {
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
int ans(int l,int r) {
int len=lg[r-l+1];
return max(st[l][len],st[r-(1<<len)+1][len]);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>cval;
for(int i=1;i<=n;i++) {
cin>>a[i];
st[i][0]=a[i];
}
init();
int sum=0,as=0;
for(int i=0;i<=n;i++) {//i~i+1
sum=0;
if(i+m-1<=n)sum=max(cval,(i+1<=i+m-1)?ans(i+1,i+m-1):0);
else
sum=max((n-m+2<=n)?ans(n-m+2,n):0,cval);
for(int j=1;j+m-1<=n;j++) {//起点
if((j<=i&&j+m-1>i)) {
sum+=max(ans(j,j+m-2),cval);
}
else
if(j+m-1<=i) {
sum+=ans(j,j+m-1);
}
else
if(j>i) {
sum+=ans(j,j+m-1);
}
}
as=max(sum,as);
}
cout<<as;
return 0;
}
但是这样的时间复杂度不足通过本题。
考虑优化。被影响的区间,不被影响的区间都是连续的。倘若我们解不等式组,可以得到不同种区间的取值范围。
- 对于被影响的区间:

浙公网安备 33010602011771号