[题解] Codeforces Round 1130 (Div. 2)C. Yet Another Black Cell Problem
题目:
受此题启发。
有一条被划分为单元格的长条,单元格从左到右编号为 $0$ 到 $10^9$。初始时,所有单元格都是白色的。
给定一个长度为 $n$ 的数组 $a$ ,其中包含 $n$ 个单元格的下标。我们希望将这 $n$ 个单元格全部染黑。
给定一个整数 $x$,你可以选择一个整数 $y$,满足 $0 \le y \le x$,然后任意选择 $y$ 个不在 $a$ 中的单元格。同时,我们也希望在保持所有未被选择的单元格为白色的情况下,将这选出的 $y$ 个单元格染黑。
你可以任意多次执行以下操作:
- 选择两个不同的单元格 $i$ 和 $j$,满足 $1 \le |i-j| \le k$,并将它们染黑,无论它们原本是黑色还是白色。
求能够将所有需要染黑的单元格染黑,同时保持其他单元格为白色的最小 $k$。
每个测试包含多个测试用例。第一行包含测试用例数 $t(1≤t≤10^4)$。测试用例的描述如下。
每个测试用例的第一行包含两个整数 n 和$ x(2≤n≤2⋅10^5,0≤x≤n)$。
第二行包含 n 个整数$ a_1,a_2,…,a_n(0≤a_i≤10^9)$。保证对于所有 $1≤i<n$,都有 $a_i<a_i+1$。
保证所有测试用例中 n 的总和不超过 $2⋅10^5$。
这是一道二分查找,二分划定染色的区块长度再核实是否可行
1.误区:为什么不能贪心堆查找
错误写法:将整个序列计算相邻距离并存入大根堆中,每次优化最长距离至原距离的1/2
题目中说明了选择距离在k以内的两个格子(不要求颜色)染为黑色,所以每个格子可以复用且黑格也可以作为起点
而对于每个格子,可以由左/右任意一侧的黑格子作为落脚点转化
所以并不是所有格子都是从距离中间向两侧优化的,这种解法只看了一个方向
2.二分答案实现
对于一个黑格$i$:当$i$两侧存在距离$<=k$的相邻黑格时认为这个黑格是相邻的,若左侧距/右侧距存在且$>k$则认为其是"孤立"的
对于孤立点i:
-
若$i<n-1$且$pos[i+1]-pos[i]<=2*k$(这个距离内可优化间距至k以内),则染色一次可以优化两侧黑格的最小相邻距离,i+=2
-
若$pos[i+1]-pos[i]>2*k$,则染色仅能优化这一个孤立点距离,i++
原题$n<2e^5$且k增大使用的优化染色个数单调减少,可以考虑二分取k值并统计使用染色的次数是否$<x$
3.源代码:
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
//思路:二分答案
void solve(){
int n,x;
cin>>n>>x;
vector<int> pre(n,0);
for(int i=0;i<n;i++){
cin>>pre[i];
}
vector<int> signle_inx; //装载孤立点
auto check=[&](int mid)->bool{
for(int i=0;i<n;i++){
//确定一点是否为孤立点 注意:一定要两侧都判定
if((i==0||pre[i]-pre[i-1]>mid)&&(i==n-1||pre[i+1]-pre[i]>mid)){
signle_inx.push_back(i);
}
}
if(signle_inx.empty()) return true; //没有孤立点,说明已实现
int sz=signle_inx.size(),used=0;
for(int i=0;i<sz;){
if(pre[signle_inx[i]]-pre[signle_inx[i-1]]<=2*mid){//可以同时优化两个点
i+=2;
}else{//仅能优化当前点
i++;
}
used++;
}
signle_inx.clear(); //清空便于下轮使用
return used<=x;
};
int l=0,r=1e9;//二分答案,上界直接定1e9(方便)
while(r-l>1){
int mid=l+(r-l)/2;
if(check(mid)){
r=mid;
}else{
l=mid;
}
}
cout<<r<<'\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
return 0;
}
浙公网安备 33010602011771号