本
题
一
看
数
据
范
围
就
知
道
大
概
是
O
(
n
l
o
g
n
)
的
算
法
,
然
后
很
自
然
就
想
到
了
二
分
,
但
关
键
是
怎
么
判
断
本题一看数据范围就知道大概是O(nlogn)的算法,然后很自然就想到了二分,但关键是怎么判断
本题一看数据范围就知道大概是O(nlogn)的算法,然后很自然就想到了二分,但关键是怎么判断
对
于
需
要
原
来
O
(
n
2
)
的
查
找
,
我
们
很
快
就
想
到
了
O
(
n
)
优
化
,
具
体
地
,
有
队
列
,
预
处
理
数
组
,
堆
…
…
的
优
化
,
这
时
我
们
发
现
,
如
果
我
们
二
分
一
个
最
大
值
我
们
就
可
以
得
到
一
些
快
速
处
理
了
对于需要原来O(n^2)的查找,我们很快就想到了O(n)优化,具体地,有队列,预处理数组,堆……的优化,这时我们发现,如果我们二分一个最大值我们就可以得到一些快速处理了
对于需要原来O(n2)的查找,我们很快就想到了O(n)优化,具体地,有队列,预处理数组,堆……的优化,这时我们发现,如果我们二分一个最大值我们就可以得到一些快速处理了
这
可
惜
在
比
赛
时
我
没
有
想
到
用
队
列
,
也
没
有
想
到
二
分
后
怎
么
计
算
答
案
这可惜在比赛时我没有想到用队列,也没有想到二分后怎么计算答案
这可惜在比赛时我没有想到用队列,也没有想到二分后怎么计算答案
正解
1. 因 为 我 们 二 分 的 是 一 个 最 大 值 满 不 满 足 , 所 以 我 们 先 对 原 数 组 排 序 , 一 遇 到 差 值 小 于 二 分 的 值 的 时 候 , 就 直 接 加 进 队 列 , 一 遇 到 大 于 二 分 的 值 的 时 候 对 首 就 出 队 列 , 这 样 就 可 以 保 证 队 列 里 的 队 尾 一 定 可 以 和 队 列 里 其 它 元 素 一 一 构 成 合 法 方 案 1.因为我们二分的是一个最大值满不满足,所以我们先对原数组排序,一遇到差值小于二分的值的时候,就直接加进队列,一遇到大于二分的值的时候对首就出队列,这样就可以保证队列里的队尾一定可以和队列里其它元素一一构成合法方案 1.因为我们二分的是一个最大值满不满足,所以我们先对原数组排序,一遇到差值小于二分的值的时候,就直接加进队列,一遇到大于二分的值的时候对首就出队列,这样就可以保证队列里的队尾一定可以和队列里其它元素一一构成合法方案
bool check(ll lim){
ansout=0;cnt=0;
ll l=1,sum=0;
for(ll r=1;r<=n;r++)
{
while(l<r&&((val[r]-val[l])>lim)) sum-=val[l],l++;//
ansout+=(r-l)*val[r]-sum;
sum+=val[r];
cnt+=(r-l);
}
if(cnt>=k) return true;
return false;
}
2.统计答案的时候,因为最大值小于等于找到的值的数可能有,可能只是个数小于k,所以我们再做一次统计就行了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1000100;
ll k,n,ansout=0,cnt;
ll val[N];
bool check(ll lim){
ansout=0;cnt=0;
ll l=1,sum=0;
for(ll r=1;r<=n;r++)
{
while(l<r&&((val[r]-val[l])>lim)) sum-=val[l],l++;//
ansout+=(r-l)*val[r]-sum;
sum+=val[r];
cnt+=(r-l);
}
if(cnt>=k) return true;
return false;
}
int main(){
scanf("%lld%lld",&n,&k);
for(ll i=1;i<=n;i++){
scanf("%lld",&val[i]);
}
sort(val+1,val+n+1);
ll l=0,r=val[n]-val[1];
ll ans=r;
while(l<=r){
ll mid=((l+r)>>1);
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
if(cnt==k){
printf("%lld",ansout);
return 0;
}
check(ans-1);
ansout+=(k-cnt)*ans;
printf("%lld",ansout);
}

899

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



