2022-ICPC(南京)D - Chat Program(二分,差分)

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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心刍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值