LOADING

加载过慢请开启缓存 浏览器默认开启

The 2022 ICPC Asia Nanjing Regional Contest

D. Chat Program

题目

You’re the researcher of the International Chat Program Company (ICPC). Today, you discover the following chat history when reviewing some research data.

SUA (2022/12/04 23:01:25)

I’m out of ideas for competitive programming problems! Please give me a problem about sequences.

BOT (2022/12/04 23:01:27)

Sure. Here is a competitive programming problem about sequences.

Given an integer sequence $a_1, a_2, \cdots, a_n$ of length $n$ and four other integers $k$, $m$, $c$ and $d$, your goal is to maximize the $k$-th largest element in the sequence.

To achieve the goal, you can perform the following operation at most once: select a continuous sub-array of length $m$ and add an arithmetic sequence with length $m$, initial term $c$ and common difference $d$ to the sub-array.

More formally, you can select an integer $p$ satisfying $1 \le p \le n - m + 1$ and add $(c + di)$ to $a_{p + i}$ for all $0 \le i < m$.

Calculate the largest possible value of the $k$-th largest element in the sequence after at most one operation.

The $k$-th largest element in the sequence is the $k$-th element in the sorted sequence after sorting all elements from the largest to the smallest. For example, the $3$rd largest element in sequence ${5, 7, 1, 9}$ is $5$, while the $3$rd largest element in sequence ${9, 7, 5, 9}$ is $7$.

SUA (2022/12/05 00:15:17)

This problem seems difficult! Please teach me the solution.

BOT (2022/12/05 00:15:30)

Sure. Firstly, we can…

[DATA EXPUNGED]

Unfortunately, parts of the chat history are lost due to a disk failure. You’re amazed at how a chat program can create a competitive programming problem. To verify whether the chat program can create valid problems, you decide to try on this problem.

image-20240917151751463

思路

二分答案,如果一个数想成为数组中的第k大,那么就需要至少k-1个比他大的数存在,在check函数中,我们把比mid大的数字状态更新为1,不做考虑,所有状态为0的点在need数组中记录达到mid所需要的最小项数,现在要做的就是维护所有状态为0的点,让他们尽可能多的变成比mid大的数字,当这些数字满足>=k时,则当前的mid是满足条件的

因为等差数列是连续加在数组上的,那么不妨把这个等差数列想象成一个滑动窗口m,m的第i个数代表等差数列的第i项,在移动窗口的时候窗口尾部每扫描到一个为0的点,判断一下他的need是否小于m的尾项,如果是,就加入到一个优先队列里面(优先队列用来存储当前元素弹出的下标idx,根据idx的大小关系排序,越小的弹出时间越早),否则直接略过,在每一次挪动窗口加入值之后用while循环判断当前有多少元素已经不满足条件。遍历一遍维护优先队列的最大size即可,加上之前已经大于mid的元素个数与k进行比较。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
int n,k,m,c,d,a[N];
bool st[N];
int need[N];
bool check(int mid){
    for(int i=1;i<=n;i++){  
        if(a[i]>=mid) st[i]=1;
        else st[i]=0;
        need[i]=0;
    }
    for(int i=1;i<=n;i++){
        if(!st[i]){
            if(mid-a[i]<=c) need[i]=1;
            else{
                if(d==0) need[i]=1e9; //公差为0时,首项不满足,之后任何一项都不会满足,所以need[i]记录无限大
                else
                need[i]=(mid-a[i]-c+(d-1))/d+1; 
            }
        }
    }
    priority_queue<int,vector<int>,greater<int>>q;
    for(int i=1;i<=m;i++){
        if(st[i]==0&&need[i]<=i) q.push(m+(i-need[i])+1);
    }
    int ans=q.size();
    for(int i=m+1;i<=n;i++){
        if(st[i]==0&&need[i]<=m) q.push(i+(m-need[i])+1);
        while(!q.empty()&&q.top()<=i){
            q.pop();
        }
        ans=max(ans,(int)q.size());
    }
    for(int i=1;i<=n;i++){
        if(st[i]) ans++;
    }
    if(ans>=k) return 0;
    else return 1;
}
void solve()
{
    cin>>n>>k>>m>>c>>d;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int l=0,r=1e17;
    while(l<r){
        int mid=l+r+1>>1;
        if(check(mid)) r=mid-1;
        else l=mid;
    }
    cout<<l<<endl;
}
signed main()
{
    int t=1;
    while(t--)
    {
        solve();
    }
}