P9936 [NFLSPC #6] 等差数列
题意:
T T T 次查询,给定一个长为 n n n 的序列,每次可以给其中一个数加一,求让序列变成单调不增的等差序列的最小操作数。 1 ≤ T ≤ 100 1\leq T\leq 100 1≤T≤100, 1 ≤ n , ∑ n ≤ 10 5 1\leq n, \sum n\leq 10 ^ 5 1≤n,∑n≤105, 1 ≤ a i ≤ 10 9 1\leq a_i\leq 10 ^ 9 1≤ai≤109。
思路:
简单推一下,发现公差
d
d
d 和答案并不是单调的。所以看看柿子吧。
设公差为
d
d
d,操作完成的序列为
s
s
s。那我们可以知道
s
i
≤
a
i
s_i\leq a_i
si≤ai,同时
s
i
=
s
1
+
(
i
−
1
)
×
d
s_i=s_1+(i-1)\times d
si=s1+(i−1)×d。 把第二个式子代入第一个,得到
s
1
+
(
i
−
1
)
×
d
≤
a
i
s_1+(i-1)\times d\leq a_i
s1+(i−1)×d≤ai。因为要求最小操作次数,所以
s
1
s_1
s1 越小越好,也就是
s
1
s_1
s1 为
a
i
−
(
i
−
1
)
×
d
a_i-(i-1)\times d
ai−(i−1)×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=1∑n[s1+(i−1)×d−ai]。因为是等差序列,再根据上面我们推出来的
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=1∑n[s1+(i−1)×d−ai]。把这个式子拆开,发现是凸的。我们可以用三分来找凸函数的最值。
注意:
如果你把最后的答案化简成带 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;
}

被折叠的 条评论
为什么被折叠?



