CF1993D-二分+dp处理中位数
CF1993D-二分+dp处理中位数
大致题意
给定两个正整数 n 和 k 以及另一个由 n 个整数组成的数组 a 。
在一次操作中,可以选择 a 的任意一个大小为 k 的子数组,然后将其从数组中删除,而不改变其他元素的顺序。更正式地说,假设是对子数组 的操作,使得 ,那么执行此操作意味着将 a 替换为 。
例如,如果 ,我们对这个数组执行 操作,它就会变成 。此外,操作 的结果是 ,操作 的结果是 。
当 a 的长度大于 k (即 )时,你必须重复操作。处理后,数组 a 中所有剩余元素的最大可能中值是多少?
- 对长度为 n 的数组中的元素按非递减顺序排序后,中位数是索引为 的元素。例如 $median([2,1,5,4,3])=3 median([5])=5 和 median([6,8,2,4])=4 $。
解题思路
我们可以通过二分答案+dp的形式来解决这个问题
通过二分中位数p,每次通过dp检查是否能够满足当前的中位数,在处理中位数一类的题目中非常常见
我们重点讨论dp的过程:
我们在处理中位数类型的dp时常见的操作是将数值根据其与中位数的关系,分别处理为,对其求前缀和,若表示当前中位数可以实现
再结合本题,本题考虑删除长度为k的子序列,通过观察我们可以发现我们需要将整个序列按照长度k划分,例如序列,如果k==2我们可以将其划分为,每一段的第一个元素()我们要特殊考虑
- 若,那么考虑两种情况:
- 作为一个最终结果的开始
- 前k个被删除,作为上上个段的一部分
- 否则也考虑两种情况:
- 继续作为上一个段的一部分:
- 前k个被删除,作为上上个段的一部分:
代码实现
#include<bits/stdc++.h> using namespace std; #define endl "\n" void solve() { int n,k; cin>>n>>k; vector<int> a(n+1); for(int i = 1;i <= n;++i) cin>>a[i]; auto check = [&](int mid) { vector<int> f(n+1,-1e9); f[0] = 0; for(int i = 1;i<= n;++i) { int val = (a[i]>=mid?1:-1); if((i-1)%k==0) { if(i-k>=0) f[i] = max(f[i-k],val); else f[i] = val; } else{ if(i-k>=0) f[i] = max(f[i-1]+val,f[i-k]); else f[i] = f[i-1]+val; } } return f[n]>0; }; int l = 1,r = 1e9+7; while(l<r) { int mid = (l+r+1)/2; if(check(mid)) l=mid; else r = mid-1; } int ans = r; cout<<ans<<endl; } int main() { ios::sync_with_stdio(false); cin.tie(0); int tt; cin>>tt; while(tt--)solve(); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效