P9936 [NFLSPC #6] 等差数列 题解

P9936 [NFLSPC #6] 等差数列

题意:

T T T 次查询,给定一个长为 n n n 的序列,每次可以给其中一个数加一,求让序列变成单调不增的等差序列的最小操作数。 1 ≤ T ≤ 100 1\leq T\leq 100 1T100 1 ≤ n , ∑ n ≤ 10 5 1\leq n, \sum n\leq 10 ^ 5 1n,n105 1 ≤ a i ≤ 10 9 1\leq a_i\leq 10 ^ 9 1ai109

思路:

简单推一下,发现公差 d d d 和答案并不是单调的。所以看看柿子吧。
设公差为 d d d,操作完成的序列为 s s s。那我们可以知道 s i ≤ a i s_i\leq a_i siai,同时 s i = s 1 + ( i − 1 ) × d s_i=s_1+(i-1)\times d si=s1+(i1)×d。 把第二个式子代入第一个,得到 s 1 + ( i − 1 ) × d ≤ a i s_1+(i-1)\times d\leq a_i s1+(i1)×dai。因为要求最小操作次数,所以 s 1 s_1 s1 越小越好,也就是 s 1 s_1 s1 a i − ( i − 1 ) × d a_i-(i-1)\times d ai(i1)×d 的最大值(题总不能越做越少吧)。
答案是什么呢?答案就是原序列变到答案序列的操作总和,也就是差值和: ∑ i = 1 n [ s 1 + ( i − 1 ) × d − a i ] \sum\limits_{i=1}^{n}[s_1+(i-1)\times d-a_i] i=1n[s1+(i1)×dai]。因为是等差序列,再根据上面我们推出来的 s 1 s_1 s1,可以得出最终答案是 ∑ i = 1 n [ s 1 + ( i − 1 ) × d − a i ] \sum\limits_{i=1}^{n}[s_1+(i-1)\times d-a_i] i=1n[s1+(i1)×dai]。把这个式子拆开,发现是凸的。我们可以用三分来找凸函数的最值。

注意:

如果你把最后的答案化简成带 n n n 的项了,注意 n n n 也要开 long   long \texttt {long long} long long

code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int N=1e5+5;
const ll inf9=1e9;
ll n;
ll a[N];
ll ans;
int T;
ll read(){
	ll x=0;
	char c=getchar();
	while(c<'0'||c>'9'){
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	return x;
}
ll solve(ll x){
	ll p=0;
	for(int i=1;i<=n;i++){
		p=max(p,a[i]+(i-1)*x);
	}
	ll sum=0;
	for(int i=1;i<=n;i++){
		sum+=p-(i-1)*x-a[i];
	}
	return sum;
}
int main(){
	scanf("%d",&T);
	while(T--){
		n=read();
		for(int i=1;i<=n;i++) {
			a[i]=read();
		}
		ll l=0,r=inf9;
		ans=0;
		while(l<r){
			ll lo=l+(r-l)/3,rr=r-(r-l)/3;
			if(solve(lo)<=solve(rr)) 	r=rr-1;
			else l=lo+1;
		}
		printf("%lld\n",solve(l));
	} 
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值