2023.9.9CSP-J模拟赛
比赛网址
我太菜了。。。。。。
T1
水题一道,跳过
T2
简单分讨,刚开始写的太复杂了。后来才发现自己是个**。
T3
给定一个序列,你可以删除一些数,求最终所有剩下的数的和的最大值,满足剩下的每相邻的两个数的和都是质数。
感觉可以二分,但是貌似没啥用。
正解应该是 DP。
两种 dp 思路,其实本质是一样的,但是反着考虑好像不能滚动数组。
- 设 \(f_{i,0/1,k}\) 表示考虑前 \(i\) 个数,删掉/保留第 \(i\) 个数,最后一个数是 \(k\) 的剩余数的和的最大值。
状态转移就有
\[f_{i,1,a_i}=\max(f_{i-1,1,a_i},f_{i-1,0,k}+a_i)\\
f_{i,0,a_i}=\max(f_{i-1,0,a_i},f_{i,1,a_i})
\]
答案为
\[\max\{f_{n,1,a_i}\}
\]
- 设 \(f_{i,0/1,k}\) 表示考虑前 \(i\) 个数,删掉/保留第 \(i\) 个数,最后一个数是 \(k\) 的删掉数的和的最小值。
初始状态为
\[f_{0,0,0}=f_{0,1,0}=1
\]
状态转移就有
\[f_{i,0,k}=\min(f_{i-1,0,k,f_{i-1,1,k}})+a_i\\
f_{i,1,a_i}=\min\{\min(f_{i-1,1,k},f_{i-1,0,k})\}
\]
答案为
\[sum-\min(\min\{f_{n,0,i}\},f_{n,1,a_n})+1
\]
T4
给定一个序列,求不小于给定长度的子区间的中位数的最大值。
二分答案,每次 \(O(n)\) check。
具体来说,对于每个小于中位数的值,我们记它的代价为 \(-1\),大于中位数的值的代价为 \(1\),判断是否有满足要求的子区间代价的和不小于 \(0\) 即可。
具体实现还是很 trick 性的,在于如何以 \(O(n)\) 的时间复杂度求子区间代价和的最值。还是看看代码吧。
点击查看代码
bool check(int x)
{
for(int i=1;i<=n;i++)
if(a[i]<x)sum[i]=sum[i-1]-1;//记录前缀和
else sum[i]=sum[i-1]+1;
int ans=0;
for(int i=m;i<=n;i++)
{
ans=min(ans,sum[i-m]);//一边记录最小前缀,一边判断区间是否符合标准,相当于一个前缀最值操作,很精妙
if(sum[i]>=ans)return 1;
}
return 0;
}
考场上没想到二分,实际上仔细想想,还是很精妙的。实际上中位数在序列中刚好也是中间的位置,这和二分的特点也是很像的。如果换成众数,实际上也就没有单调性了,也根本用不到二分。

浙公网安备 33010602011771号