题意:
数据范围:n,k<=5e5, 1<=a[i]<=1e9
解法:
二分答案mid:
令c[i]=(a[i]>=mid)?1:-1.
判断mid是否满足条件,判断最优操作后剩余元素的和是否>=0即可.
如何计算最优操作后剩余元素的和?
最后会剩余个数为rem=(n%k==0)?k:n%k
关键结论:
最后剩余的rem个数,第i个数的下标id_i一定满足id_i%k==i
因为每次删除k个连续的数, 最后剩余的第i个数原下标一定为t_i*k+i.
且t_i>=t_(i-1)
我们可以按i%k将数组分为k组, 在前rem组分别选出一个数,满足总和最大即可.
由于最后剩余的第i个数的下标一定是大于第i-1个数的,因此这里有一个隐含条件是:
设idx[i]为第i组选出的数的下标,则需要满足idx[i]>idx[i-1].
为了计算出满足条件的最大剩余元素和, 一个dp的做法是:
令d[i][j]为前i个数, 选出j个数的最大总和.
转移方程:
1. 不选i:
d[i][j]=d[i-1][j]
2. 选i:
d[i][i%k]=max(d[i][i%k],d[i-1][i%k-1]+c[i]).
dp数组的第一维可以直接优化掉, 即:
令d[j]表示选出j个数的最大总和.
如果i%k=1:
d[i%k]=max(d[i%k],c[i])
否则:
d[i%k]=max(d[i%k],d[i%k-1]+c[i]).
d[rem]就是最优操作下的最大剩余总和.
dp复杂度O(n).
二分+dp的算法总复杂度为O(n*log)
Code:
#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define int long long
#define PI pair<int, int>
const int maxm=5e5+5;
// const int mod=998244353;
const int mod=1e9+7;
int n,k;
int a[maxm];
int b[maxm];
int c[maxm];
int d[maxm];
int check(int mid){
for(int i=1;i<=n;i++){
c[i]=(a[i]>=mid)?1:-1;
}
int rem=n%k;
if(rem==0)rem=k;
for(int i=1;i<=k;i++){
d[i]=-1e9;
}
for(int i=1;i<=n;i++){
int st=i%k;
if(st==0)st=k;
if(st==1){
d[st]=max(d[st],c[i]);
}else{
if (d[st-1]!=-1e9){
d[st]=max(d[st],d[st-1]+c[i]);
}
}
}
return d[rem]>0;
}
void solve() {
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
if (k>=n){
// 特判k>=n的情况
sort(a+1,a+1+n);
cout<<a[(n+1)/2]<<endl;
return ;
}
for(int i=1;i<=n;i++){
b[i]=a[i];
}
sort(b+1,b+1+n);
int l=1,r=n;
int ans=-1;
while(l<=r){
int mid=(l+r)/2;
if(check(b[mid])){
ans=b[mid];
l=mid+1;
}else{
r=mid-1;
}
}
cout<<ans<<endl;
}
signed main() {
#define MULTI_CASE
ios::sync_with_stdio(0);
cin.tie(0);
#ifndef ONLINE_JUDGE
freopen("../in.txt", "r", stdin);
freopen("../out.txt", "w", stdout);
#endif
#ifdef MULTI_CASE
int T;
cin >> T;
while (T--)
#endif
solve();
return 0;
}