1.参考了此dl的博客,首先证明选择的数一定在序列内:现在有两个数
a
,
b
(
a
<
b
)
a,b(a<b)
a,b(a<b),a的数量是
x
x
x个,b的数量是
y
y
y个,现在在
a
−
b
a-b
a−b之间选取一个数
c
c
c,假定最后答案就是
c
c
c,那么花费为
(
c
−
a
)
∗
x
+
(
b
−
c
)
∗
y
(c-a)*x + (b-c)*y
(c−a)∗x+(b−c)∗y,选取
c
=
a
c=a
c=a或者
c
=
b
c=b
c=b时花费为
(
b
−
a
)
∗
m
i
n
(
x
,
y
)
(b-a)*min(x,y)
(b−a)∗min(x,y),显然
(
b
−
a
)
∗
m
i
n
(
x
,
y
)
≤
(
c
−
a
)
∗
x
+
(
b
−
c
)
∗
y
(b-a)*min(x,y) \leq (c-a)*x + (b-c)*y
(b−a)∗min(x,y)≤(c−a)∗x+(b−c)∗y。那么最终答案一定是原数组存在的数
2.那么显然就是考虑序列的每一个数,显然需要构造是数量是 k k k减去这个数在序列出现的次数,那么我们排序后处理一下位置即可。首先比当前 a [ i ] a[i] a[i]小的要想达到 a [ i ] a[i] a[i],首先必须都达到 a [ i ] − 1 a[i]-1 a[i]−1,因为每次只能操纵最小值;而右半部分想要达到 a [ i ] a[i] a[i],首先必须都达到 a [ i ] + 1 a[i]+1 a[i]+1,因为每次只能操纵最大值。那么左边的贡献是左边数量乘以 a [ i ] − 1 a[i]-1 a[i]−1减去前缀和,右边同理区间和减去右边的数量乘以a[i]+1,这样提前处理前缀和即可。最后是分成三种情况
- 只要左边就能构成 k k k个数
- 只要右边就能构成 k k k个数
- 两边都需要
3.时间复杂度显然仅 O ( n ) O(n) O(n)
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=2e5+10;
int a[maxn],L[maxn],R[maxn];
ll sum[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,k;
cin>>n>>k;
memset(sum,0,sizeof sum);
memset(L,0,sizeof L);
memset(R,0,sizeof R);
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++){
L[i]=(a[i]==a[i-1]?L[i-1]+1:1); //记录每个位置左有连续几个这样的数
sum[i]=sum[i-1]+a[i]; //记录前缀和
}
for(int i=n;i>=0;i--){
R[i]=(a[i]==a[i+1]?R[i+1]+1:1); //记录每个位置右有连续几个这样的数
}
ll ans=INF;
for(int i=1;i<=n;i++){
int l=i-1,r=i+1,now=0,cnt=k-1,gl=a[i]-1,gr=a[i]+1; //l和r记录位置;cnt为k-1因为除a[i]本身外还需构造出k-1个数;gl是左半部分需要达到的大小,gr是右半部分需要达到的大小
int p=l,q=r;
if(a[l]==a[i]) now+=L[l],l-=L[p];
if(a[r]==a[i]) now+=R[r],r+=R[q];
if(now>=cnt){ //当前a[i]的个数可以达到了k,无需构造
ans=0;
break;
}
cnt-=now; //还需构造几个a[i]
int numl=l,numr=n-r+1; //左半部分和右半部分分别需要的构造数
ll cl=1LL*gl*numl-sum[l]; //左贡献
ll cr=sum[n]-sum[r-1]-1LL*gr*numr; //右贡献
if(numl>=cnt) ans=min(cl+cnt,ans); //左边能独立完成多余k-1个数为一种情况,同理右边
if(numr>=cnt) ans=min(cr+cnt,ans);
ans=min(cl+cr+cnt,ans); //两边都需要
}
cout<<ans<<endl;
return 0;
}