您的位置:首页 > 其它

【bzoj 十连测】[noip2016十连测第三场]Problem A: 平均数(二分答案+归并排序求逆序对)

2016-11-17 07:42 423 查看


Problem A: [noip2016十连测第三场]平均数

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 165  Solved: 51

[Submit][Status][Web
Board]


Description

有一天,小A得到了一个长度为n的序列。他把这个序列的所有连续子序列都列了出来,并对每一个子序列都求了其

平均值,然后他把这些平均值写在纸上,并对它们进行排序,最后他报出了第k小的平均值。你要做的就是模仿他

的过程。


Input

第一行两个整数n,k,意义如题中所述。
第二行n个正整数,即为小A得到的序列。


Output

一行一个实数,表示第k小的平均值,保留到小数点后4位。

【 数据范围与约定】

对于 40%的数据, n≤1000

对于 100%的数据, n≤100000, k≤n*(n+1)/2, 序列中的数≤10^9


Sample Input

6 10
3 5 4 6 1 2


Sample Output

3.6667


HINT

【题解】【二分答案+归并排序求逆序对】
[谁告诉我这是day1 T1!!!!!! 打死它打死它!]
【看看数据范围,呵呵!枚举必然TTT】
【二分答案,二分平均数,每二分到一个答案,就把每个元素都减去当前的平均数,并求前缀和。当前情况下,和小于等于0的区间是满足条件的,求当前有多少个区间满足条件。我们要求的最后答案,是满足有>=k个区间的最小值】
【但是,不能直接枚举区间,会T,因为每个区间[i,j]可以用sum[j]-sum[i-1]求出,所以直接求逆序对数即可】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define esp 1e-6
using namespace std;
ll k,num;
int n,a[100010],maxn;
double ans,sum[100010],tmp[100010];
inline double abss(double x)
{
if(x>=0) return x;
else return -x;
}
void qsort(int f,int l)
{
if(f<l)
{
int mid=(f+l)>>1;
qsort(f,mid); qsort(mid+1,l);
int i=f,k=f,j=mid+1;
while(i<=mid&&j<=l)
if(sum[j]-sum[i]<=esp)
{
tmp[k++]=sum[j++];
num+=(ll)mid-i+1;
}
else tmp[k++]=sum[i++];
while(i<=mid) tmp[k++]=sum[i++];
while(j<=l) tmp[k++]=sum[j++];
for(i=f;i<=l;++i) sum[i]=tmp[i];
}
}
inline bool check(double x)
{
memset(tmp,0,sizeof(tmp));
for(int i=1;i<=n;++i) sum[i]=sum[i-1]+(double)(a[i]-x);
num=0; qsort(1,n);
for(int i=1;i<=n;++i)
if(sum[i]<=0) num++;
return (num>=k);
}
inline void find(double l,double r)
{
double mid;
while(!(abss(r-l)<=esp))
{
mid=(l+r)/(2.0);
if(check(mid)) ans=mid,r=mid;
else l=mid;
}
}
int main()
{
//freopen("int.txt","r",stdin);
//freopen("my.txt","w",stdout);
int i;
scanf("%d%lld",&n,&k);
for(i=1;i<=n;++i)
{
scanf("%d",&a[i]);
if(maxn<a[i]) maxn=a[i];
}
find(0,maxn);
printf("%.4lf\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  bzoj 二分答案
相关文章推荐