[COCI 2022/2023 #5] Slastičarnica
这题很好的 dp 题
首先我们知道这个 \(q\) 数据范围明显是诈骗的,最多就跟 \(n\) 同阶。
那我们的复杂度就大概是 \(O(n^2)\)
很明显使用 dp 来实现这个过程的,设一个 \(dp_{i,l,r}\) 表示在第 \(i\) 次中这个区间为 \(l\sim r\) 是否可能,这个大概是一个 \(O(n^4)\) 的时间复杂度。
考虑优化,我们发现这个 dp 数组只存布尔值,这就很浪费了(当然这里很明显不能用 \(bitset\) ),很容易想到在第 \(i\) 次操作中 \(r\) 越大越优,那我们只需存一个 \(dp_{i,l}\) 表示最大的右端点即可。
那我们分别考虑左边的转移和右边的转移。
对于左边的转移很简单,\(dp_{i,j} = \max(dp_{i-1,l}) l\in [1,j-d]\and mi_{j-d-1,j-1}\ge s\),这里可以用一个前缀最值来实现。可以优化到 \(O(n)\)。
而右边的转移 \(dp_{i,j} = max(r) r\in [j,dp_{i-1,j}-d]\and mi_{r+1,r+d+1}g\ge s\),这个优化其实也不难,观察到它取的是一个 \(mi_{r+1,r+d+1}\) ,我们可以再搞一个数组 \(h_r\) ,表示原来以 \(r\) 为右端点,最大的值 \(h_r\) 能让 \(l \sim r\) 转移到 \(l\sim h_r\)。这个是一个很简单的 dp,不赘述了。
总体的时间复杂度 \(O(n^2)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
int f[N][N],n,q,a[N],mi[N][N];
int pre[N],h[N];
//表示第i次操作,左端点为 l 的最大的
signed main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
for(int i=1;i<=n;i++){
mi[i][i]=a[i];
for(int j=i+1;j<=n;j++)
mi[i][j]=min(mi[i][j-1],a[j]);
}
f[0][1]=n;
int i=1;
for(int d,s,sum=0;i<=n;i++){
scanf("%d%d",&d,&s);//长度和最小值
sum+=d;
if(sum>n)break;
for(int l=1,c=0;l<=n;l++){
pre[l]=max(f[i-1][l],pre[l-1]);
a[l]>=s?c++:c=0;
h[l]=c>=d?l-d:h[l-1];
}
for(int l=1;l<=n;l++){
f[i][l]=-0x3f3f3f3f;
if(l-d>=1 && mi[l-d][l-1]>=s)
f[i][l]=max(f[i][l],pre[l-d]);
//左边的删掉
if(pre[l]>=l && h[pre[l]]>=l)
f[i][l]=max(f[i][l],h[pre[l]]);
}
bool op = false;
for (int l = 1; l <= n && !op; ++l)
op |= f[i][l] >= l-1 ;
if (!op) break;
}
//f_{i,j} = max(f_{i-1,l}) l\in [1,j-d]^mi[j-d-1,j-1]>=s
//f_{i,j} = max(r) r\in [j,f_{i-1,j}-d]^mi[r+1,r+d+1]>=s
cout<<i-1;
return 0;
}

浙公网安备 33010602011771号