【题解】CF1993D

翻译

原题链接
在这里插入图片描述

思路

  容易发现,无论如何操作,最后剩下的数量是一定的,记剩下的数组中中位数的位置为 m m m(从1开始记),注意不能将数组删空。有:

剩余数组的长度 L = ( n − 1 ) m o d    k + 1 L=(n-1) \mod k + 1 L=(n1)modk+1
m = ( L + 1 ) / 2 m = (L + 1) / 2 m=(L+1)/2

  显然我们需要一个 O ( n l o g n ) O(nlogn) O(nlogn)的算法,根据经验,注意到中位数具有可二分性(显然尽量把小的数删掉中位数肯定大)。所以二分答案中位数是多少,然后 c h e c k check check这个中位数是否可行。

  对于判断环节,我们可以设计一个函数,寻找一种方案,使删除后剩下的数中小于 x x x的最小数量 c n t cnt cnt,那如果 c n t < m cnt < m cnt<m,,则说明存在可行的中位数 y y y,使得 y < x y<x y<x。根据这些,我们可以写出二分的框架:

int count(int x) {
	return 剩下的数中小于x的最小数量cnt
} 

int m = ((n-1) % k + 1 + 1) / 2;
int l = 1, r = 1e9, ans=-1;
while(l<=r) {
	int mid = l+r>>1;
	if(count(mid) < m) {   // 最少比mid小的数量比m少 
		ans = mid;
		l = mid + 1; 
	} else {
		r = mid - 1;
	}
} 
cout<<ans<<endl;

  接下来完善 c o u n t count count函数。采用 d p dp dp的方式,记 f [ i ] f[i] f[i]表示到第 i i i个数时小于 x x x的数量的最小值(允许不拿 a [ i ] a[i] a[i]),则:

f [ i ] = m i n ( f [ i − 1 ] + ( a [ i ] < x ? 1 : 0 ) , f [ i − k ] ( i f i > = k ) ) f[i]=min(f[i-1]+(a[i]<x?1:0), f[i-k] \quad (if \quad i>=k)) f[i]=min(f[i1]+(a[i]<x?1:0),f[ik](ifi>=k))

  但这样会有一个问题,如果 k ∣ n k | n kn,则这个 d p dp dp会找到一种方案,将所有数都删除,最终返回 0 0 0
  为了解决这个问题,我们标记一下当前方案是否为空即可,即将 f f f数组新增大小为 2 2 2的一维,最后返回 f [ n ] [ 1 ] f[n][1] f[n][1]。递推式子稍微改一改即可,代码如下:

for(int i=0;i<=n;i++) f[i][0] = f[i][1] = 1e9;
f[0][0] = 0;
for(int i=1;i<=n;i++) {
	f[i][1] = min(f[i-1][0], f[i-1][1]) + (a[i] < x ? 1 : 0);
	if(i>=k) {
		f[i][0] = min(f[i][0], f[i-k][0]);
		f[i][1] = min(f[i][1], f[i-k][1]);
	}
} 
return f[n][1];

总代码

#include<bits/stdc++.h>
#define N 500005
using namespace std;
int t, n, k, a[N], f[N][2];
int count(int x) {
	// 使小于x的数最少
	// f[i]表示到i,i可以被删除 , 标记是否为空 
	for(int i=0;i<=n;i++) f[i][0] = f[i][1] = 1e9;
	f[0][0] = 0;
	for(int i=1;i<=n;i++) {
		f[i][1] = min(f[i-1][0], f[i-1][1]) + (a[i] < x ? 1 : 0);
		if(i>=k) {
			f[i][0] = min(f[i][0], f[i-k][0]);
			f[i][1] = min(f[i][1], f[i-k][1]);
		}
	} 
	return f[n][1];
} 
int main() {
	cin>>t;
	while(t--) {
		cin>>n>>k;
		int m = ((n-1) % k + 1 + 1) / 2;  // 第m个数为中位数
		for(int i=1;i<=n;i++) {
			cin>>a[i];
		}
		int l = 1, r = 1e9, ans=-1;
		while(l<=r) {
			int mid = l+r>>1;
//			printf("%d %d\n", mid, count(mid));
			if(count(mid) < m) {   // 最少比mid小的数量比m少 
				ans = mid;
				l = mid + 1; 
			} else {
				r = mid - 1;
			}
		} 
		cout<<ans<<endl;
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值