Codeforces1993 D. Med-imize(二分+思维+dp,dp好题)

题意:

在这里插入图片描述
数据范围: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;
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值