[2017CCPC哈尔滨]B. K-th Number
题意是给你一个长度为N的数组,你在每个长度大于等于K的连续区间里,取出第K大的数,组成一个新数组。问新数组中第M大的数是什么。
这道题最最重要的就是,尺取法可以求出第k大的数大于等于x的区间数。
为什么可以呢?假设我们尺取法已经取到了K个大于等于x的数的区间,当我们右指针往后移动的时候,新加入的数对这个区间产生的贡献无非两种:
1. 使第K大的数变大
2. 第K大的数不变
我们发现这两种贡献都是不影响我们定义的第k大的数大于等于x区间。于是我们就只需要不断计算每个左区间开始的,符合条件的区间数即可。
之后我们讲一讲二分是怎么二分的。首先给出答案,二分这个X是什么。即二分答案。
为什么可以呢?首先我们发现,当X变大的时候,符合条件的区间数会减小,而X变小的时候,符合条件的区间数会变大。这个条件是满足单调性的。所以可以二分。
那么怎么二分呢?我们需要找的是,新数组中第M个数,这就说明大于X的区间起码要有M-1个,大于等于X的区间起码要有M个。
于是我们就得到了二分最终位置。
下面给出AC代码,注意有一个坑点就是M有可能是大于int的。
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f; ///1061109567 const int maxn = 2e6 + 10; ll n, k, m, a[maxn]; ll cal(ll x) { ll L = 1, cnt = 0; ll ans = 0; for (ll R = 1; R <= n; ++ R) { if (a[R] >= x) cnt ++; while (L <= R && cnt >= k) { ans += (n-R+1); if (a[L] >= x) cnt --; L ++; } } // cout << x << " " << ans << endl; return ans; } int main() { int T; scanf("%d", &T); while (T--) { scanf("%lld%lld%lld", &n, &k, &m); for (int i = 1; i <= n; ++ i) { scanf("%lld", &a[i]); } ll L = 1, R = 1e18, ans = 1; while (L <= R) { ll mid = L + R >> 1; if (cal(mid) < m) { R = mid - 1; } else { ans = max(ans, mid); L = mid + 1; } } // cout << cal(2) << endl; printf("%lld\n", ans); } return 0; }
分类:
日拱一卒,功不唐捐
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人