您的位置:首页 > 其它

[BZOJbegin][NOIP十连测第三场]平均数(二分+归并排序求逆序对)

2016-11-16 19:42 477 查看

题目描述



题解

首先二分一个答案k,将序列中的数都减去k,然后求前缀和。

可以发现平均数小于k的子序列只可能是Si>Sj且i<j的。也就是序列中的逆序对数。

因为是实数二分+判定,用归并排序求逆序对即可。

注意判断精度。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 100005
#define LL long long

const double eps=1e-6;
int n;
LL k,rev;
double Max,ans;
double a
,b
,c
,s
;

int dcmp(double x)
{
if (x<=eps&&x>=-eps) return 0;
if (x>0) return 1;
else return -1;
}
void mergesort(int l,int r)
{
if (l==r) return;
int mid=(l+r)>>1;
mergesort(l,mid);
mergesort(mid+1,r);

int i=l,j=mid+1,now=l-1;
while (i<=mid&&j<=r)
{
if (dcmp(s[j]-s[i])>=0) c[++now]=s[i],i++;
else c[++now]=s[j],j++,rev+=mid-i+1;
}
while (i<=mid) c[++now]=s[i],i++;
while (j<=r) c[++now]=s[j],j++;
for (int i=l;i<=r;++i) s[i]=c[i];
}
bool check(double x)
{
for (int i=1;i<=n;++i) b[i]=a[i]-x;
memset(s,0,sizeof(s));
for (int i=1;i<=n;++i) s[i]=s[i-1]+b[i];

rev=0;
for (int i=1;i<=n;++i) if (dcmp(s[i])<=0) rev++;
mergesort(1,n);
if (rev<k) return false;
else return true;
}
double find()
{
double l=0,r=Max,mid,ans;
while (dcmp(r-l)!=0)
{
mid=(l+r)/(2.0);
if (check(mid)) ans=mid,r=mid;
else l=mid;
}
return ans;
}
int main()
{
scanf("%d%lld",&n,&k);
for (int i=1;i<=n;++i) scanf("%lf",&a[i]),Max=max(Max,a[i]);

ans=find();
printf("%0.4lf\n",ans);
}


总结

①减去一个数求前缀和——平均数常用套路。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: