Range and Partition (构造+尺取)
题目大意:
给一个长度为 的数组 , 找到一个区间 [x,y], 使得 a 可以分成 k 个子数组,满足 3 个条件:
- 子数组是 a 中连续的元素组成的
- a 中每个元素分到某个子数组里
- 在每个子数组中,位于[x,y] 区间内的元素个数要严格大于剩余元素个数。
最小化
数据范围
n 不超过 1<=a[i]<=n
输入输出
输入: 样例个数t n k 数组a
输出: x,y 每个子数组的两端
解题思路
- 考虑区间 [x,y] 固定时问题的解法。假设有解,那么考虑构造,只需从头到尾,每次遍历到满足要求的数比剩余数多1时,划分出这一个数组。划分出(k-1)个数组时,最后一个单独留出来即可。
- 那么 区间[x,y]在什么时候有解? 既然要求 每个子数组在[x,y]内的数目严格大,也就是每个子数组内这样的数比其他数至少多1。划分为k个子数组,则这样的数比其他数至少多k。所以设整个数组有个数在区间内,若满足 即 即可。
- 因此,可以先遍历一遍数组 a ,得到每个数的数目的数组 b ,然后用尺取法遍历 b ,得到差最小的 x , y。最后再用x,y划分出最终结果。
复杂度
代码
#include<bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long int n,k; int a[200005]; int b[200005]; int x,y; signed main() { int t;cin >> t; while(t--) { cin >> n >> k; for(int i=1;i<=n;++i) { cin >> a[i]; b[i] = 0; } for(int i=1;i<=n;++i) b[a[i]]++; int le=1,ri=1; int sum=0,p=ceil((double)(n+k)/2.0); int minlen = 1234456778; while(ri<=n) { while(ri<=n&&sum<p) sum += b[ri++]; if(sum>=p&&ri-le<minlen) {minlen=ri-le;x=le;y=ri-1;} while(le<ri&&sum>=p) { sum -= b[le++]; if(sum>=p&&ri-le<=minlen) {minlen=ri-le;x=le;y=ri-1;} } } cout << x << " " << y << endl; int ins=0,outs=0; int num = 0; le = 1; for(int i=1;i<=n;++i) { if(num==k-1) break; if(a[i]<=y&&a[i]>=x) ins++; else outs++; if(ins>outs){ cout << le << " " << i << endl; le = i + 1; ins = outs = 0; num++; } } cout << le << " " << n << endl; } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】