您的位置:首页 > 其它

noip模拟 平均数【二分+归并排序】

2018-01-17 16:38 323 查看

Description

有一天,小A得到了一个长度为n的序列。

他把这个序列的所有连续子序列都列了出来, 并对每一个子序列

都求了其平均值, 然后他把这些平均值写在纸上, 并对它们进行排序,

最后他报出了第k小的平均值。

你要做的就是模仿他的过程



思路

题解一:实数二分+树状数组+排序。

先分析一下题目,如果是暴利枚举的话肯定会TLE。所以我们只能考虑二分答案,我们二分到一个平均数,如何判断他是否可行呢?加上当前的平均数为x,那么我们对于每一个数都减去x,对于每一个位置求前缀和,然后将得到的前缀和排序。通过前缀和作差我们是可以得到一段区间的总和的,我们要求平均数<=x的子序列个数,那么在所有数减去x的情况下就是求有多少区间的和是<=esp(精度误差),那么我们将前缀和按照从大到小排序,当前位置之前有多少在数列中位置小于当前位置的数就是对答案的贡献,可以用树状数组来统计答案。

时间复杂度Θ(k∗n∗(log2n)2)所以这样做在不开O2的情况下会TLE。

代码1

#include <bits/stdc++.h>
#define N 100005
#define ll long long
#define eps 1e-5
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
using namespace std;
inline int read(){
int ret=0,f=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())ret=(ret<<3)+(ret<<1)+c-'0';
return ret*f;
}
int n;
ll k,a
,tr
;
struct pppp{
double x;int pos;
}p
;
bool cmp(pppp a,pppp b){
return a.x-b.x>eps||a.x-b.x<=eps&&a.x-b.x>=-eps&&a.pos<b.pos;
}
inline void add(int x){
for(int i=x;i<=n;i+=i&-i)++tr[i];
}
inline int qh(int x){
ll an=0;
for(int i=x;i;i-=i&-i)an+=tr[i];
return an;
}
bool check(double x){
p[0].x=0;p[0].pos=0;
for(int i=1;i<=n;++i){
p[i].x=p[i-1].x+a[i]-x;
p[i].pos=i;
tr[i]=0;
}
sort(p+1,p+n+1,cmp);
add(p[1].pos);ll t=0;
if(p[1].x<=eps)t++;
for(int i=2;i<=n;++i){
if(p[i].x<=eps)t++;
t+=qh(p[i].pos);
add(p[i].pos);
}
if(t>=k)return true;
return false;
}
int main(){
n=read();k=read();double l=1e9,r=0;
for(int i=1;i<=n;++i)a[i]=read(),l=min(l,a[i]),r=max(r,a[i]);
double ans=1e9;
while(r-l>=eps){
double mid=(l+r)/2;
if(check(mid))ans=min(ans,mid),r=mid;
else l=mid;
}
printf("%.4lf",ans);
return 0;
}


题解二:实数二分+归并排序

上一种做法常数比较大,因为排序+树状数组相当于是两倍的常数。

其实排序和树状数组就是为了求逆序对数,我们可以利用归并排序直接求,这样就只有一倍的常数,时间复杂度Θ(n∗(log2n)2)就可以过了。

代码2

#include <bits/stdc++.h>
#define N 100005
#define ll long long
#define eps 1e-5
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
using namespace std;
inline int read(){
int ret=0,f=1;char tr=getchar();
for(;!isdigit(tr);tr=getchar())if(tr=='-')f=-1;
for(;isdigit(tr);tr=getchar())ret=(ret<<3)+(ret<<1)+tr-'0';
return ret*f;
}
int n;
ll k,a
,ans;
double tr
,b
;
inline void fz(int l,int r){
if(l==r)return ;
int mid=(l+r)>>1;
fz(l,mid);fz(mid+1,r);
int i=l,j=mid+1,k=l-1;
while(i<=mid&&j<=r){
if(b[i]<b[j])tr[++k]=b[i++];
else tr[++k]=b[j++],ans+=mid-i+1;
}
while(i<=mid)tr[++k]=b[i++];
while(j<=r)tr[++k]=b[j++];
for(i=l;i<=r;i++)b[i]=tr[i];
}
ll check(double x){
b[0]=0;
for(int i=1;i<=n;i++)b[i]=a[i]-x+b[i-1];
ans=0;fz(0,n);
return ans;
}
int main(){
n=read();k=read();double l=1e9,r=0;
for(int i=1;i<=n;++i)a[i]=read(),l=min(l,a[i]),r=max(r,a[i]);
double jg=1e9;
while(r-l>=eps){
double mid=(l+r)/2;
if(check(mid)>=k)jg=min(jg,mid),r=mid;
else l=mid;
}
printf("%.4lf",jg);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: