Gym - 104128D
题目
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 a1,a2,⋯,ana1,a2,⋯,an of length nn and four other integers kk, mm, cc and dd, your goal is to maximize the kk-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 mm and add an arithmetic sequence with length mm, initial term cc and common difference dd to the sub-array.
More formally, you can select an integer pp satisfying 1≤p≤n−m+11≤p≤n−m+1 and add (c+di)(c+di) to ap+iap+i for all 0≤i<m0≤i<m.
Calculate the largest possible value of the kk-th largest element in the sequence after at most one operation.
The kk-th largest element in the sequence is the kk-th element in the sorted sequence after sorting all elements from the largest to the smallest. For example, the 33rd largest element in sequence {5,7,1,9}{5,7,1,9} is 55, while the 33rd largest element in sequence {9,7,5,9}{9,7,5,9} is 77.
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.
题意
给一个长度为n的数组,问最多对一段区间添加等差数列后的最大的第 k 大是多少。
等差数列首项为c, 公差为d,长度为m。
思路
二分答案,利用等差数列的性质和差分的思想去维护f这个差分数组
f做前缀和后的含义是,以i位为等差数列的首项可以产生的贡献
对于第k大,只要处理后区间>=mid的个数多于k个即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+100;
int n,k,m,c,d;
int a[N];
int f[N];
bool check(int x) {
memset(f,0,sizeof(f));
int cnt=0;
for(int i=1; i<=n; i++) {
if(a[i]>=x)cnt++;
}
if(cnt>=k)return true;//原有的个数已经大于k
for(int i=1; i<=n; i++) {
if(a[i]<x) {
int l,r,p,maxx,minn;
l=max(i-m+1,0ll);//最远可到的左端点
maxx=a[i]+c+d*(i-l);//i位置最大可以到的值
if(maxx<x)continue;
minn=a[i]+c;//先加上首项
if(d==0)p=0;
else p=(x-minn-1)/d+1;//p指需要加几次公差才能大于等于mid
p=max(p,0ll);
r=i-p;//r之后的点开始为首项无法使得i位置大于等于mid
f[l]++;
f[r+1]--;
}
}
int res=0;
for(int i=1; i<=n; i++) {
f[i]+=f[i-1];
res=max(res,f[i]);
}
return res+cnt>=k;
}
void solve() {
cin>>n>>k>>m>>c>>d;
for(int i=1; i<=n; i++)cin>>a[i];
int l=0,r=1e18,mid;
while(l<=r) {
mid=(l+r)/2;
if(check(mid))l=mid+1;
else r=mid-1;
}
cout<<r<<'\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
int t=1;
// cin>>t;
while(t--)solve();
return 0;
}