您的位置:首页 > 其它

【NOIP模拟】 (11.2) T2最佳序列

2017-11-03 07:50 393 查看

最佳序列

题目描述:
       N个数,从中选择长度不小于L且不大于R的连续子序列,求出子序列平均数的最大值。

输入格式:
       输入文件的第一行包括3个整数N,L,R。
       第二行包括N个数,按顺序依次表示序列A的每一项。

输出格式:
       输出文件包括一行,一个实数,保留四位小数。

数据范围:
       20%的数据满足:1<=N<=200;R=N。
       40%的数据满足:1<=N<=2000。
       100%的数据满足:1<=n<=20000;1<=L<=R<=N;0<=Ai<=1000000

解析:
       对于20%的数据,暴力枚举即可。
       对于40%的数据,可以用前缀和的方式降维(但这道题在暴力进行优化后可以拿满......)。
       对于100%的数据,二分答案是很好想到的,但这道题的关键在于如何验证。于是我们要进行推理:
       假设这时我们二分到的平均数为x,那么如果满足条件,在这个序列中一定有一个长度为L~R的子序列满足平均数大于等于x,如果用暴力搜索肯定超时,所以我们来思考一下若当前搜索到了i作为右端点,左端点一定在i-R+1~i-L+1这个区间里,如果满足条件,那么
       sum[i]-sum[i-R]>=(i-R)*x,sum[i]-sum[i-L]>=(i-L)*x(sum为前缀和)
       我们把等式右边拆开移到左边就变成了
       sum[i]-i*x-(sum[i-R]-R*x)>=0,右边的不等式同理
       拆开后相当于
       sum[i]'-sum[i-R]'>=0,右边的不等式同理
       所以我们只用将sum都减去x,再计算出的sum[i-R]~sum[i-L]中如果最小的sum[j]能使得不等式成立,说明满足提议,x是存在的,否则是不存在的。那么问题来了,如何求出在一个长度固定的区间求最小的值,这里出现了两种方法:一种是线段树,一种是单调队列。
       线段树很好想到,因为这个问题是一个很裸的模板题,将sum减去x后存进线段树,然后枚举i时进行查找最小值即可。时间复杂度O(N (log N)^2)。
       这道题能用单调队列的原因是满足长度固定,而且每次取最小值,满足单调性,所以可以用单调队列实现。时间复杂度O(N log N)

代码(暴力100分....)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <ctime>
#include <sstream>
using namespace std;

const int Max=200010;
int n,L,R;
double l,r,mid;
double ans;
long long num[Max],sum[Max];

inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}

inline int check()
{
for(register int i=L;i<=R;i++)
{
long long maxx=0;
for(register int j=i;j<=n;j++)
maxx=max(sum[j]-sum[j-i],maxx);
ans=max((double)(maxx)/(double)(i),ans);
}
}

int main()
{
//freopen("seq.in","r",stdin);
//freopen("seq.out","w",stdout);

n=get_int();
L=get_int();
R=get_int();
for(register int i=1;i<=n;i++) num[i]=get_int();
for(register int i=1;i<=n;i++) sum[i]=sum[i-1]+num[i];
l=0;
r=1e7;
check();
printf("%0.4f\n",ans);
return 0;
}


代码(二分+单调队列):
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <ctime>
#include <sstream>
using namespace std;

const int Max=200010;
int n,L,R;
double l,r,mid;
int maxx;
int num[Max];
double sum[Max];

inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}

inline int check(double x)
{
static int p[Max];
for(int i=1;i<=n;i++) sum[i]=(double)num[i]-x;
for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
int head=1,tail=0;
for(int i=L;i<=n;i++)         //注意要从L开始而不能从L+1开始,不然会漏算1~L的情况
{
while(head<=tail&&sum[p[tail]]>=sum[i-L]) tail--;
p[++tail]=i-L;
while(head<=tail&&i-R>p[head]) head++;
if(sum[p[head]]<=sum[i]) return 1;
}
return 0;
}

int main()
{
//freopen("seq.in","r",stdin);
// freopen("seq.out","w",stdout);

n=get_int();
L=get_int();
R=get_int();
for(int i=1;i<=n;i++) {num[i]=get_int();maxx=max(maxx,num[i]);}
l=0;
r=maxx;
for(int i=1;i<=50;i++)
{
mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%0.4f\n",l);
return 0;
}


代码(二分+线段树)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <ctime>
#include <sstream>
using namespace std;

const int MAX=1e9;
const int Max=200010;
int n,L,R;
double l,r,mid;
int maxx;
int num[Max];
double sum[Max];
struct shu{int l,r;double minn;};
shu tree[Max*4];

inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}

inline void build(int root,int L,int R)
{
tree[root].l=L;
tree[root].r=R;
if(L==R)
{
tree[root].minn=sum[L];
return;
}
int mid=(L+R)>>1;
build(root<<1,L,mid);
build(root<<1|1,mid+1,R);
tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn);
}

inline double Q(int root,int L,int R,int l,int r)
{
if(l>R||r<L) return MAX;
if(l<=L&&r>=R)
{
return tree[root].minn;
}
int mid=(L+R)>>1;
if(r<=mid) return Q(root<<1,L,mid,l,r);
else if(l>mid) return Q(root<<1|1,mid+1,R,l,r);
else return min(Q(root<<1,L,mid,l,r),Q(root<<1|1,mid+1,R,l,r));
}

inline int check(double x)
{
memset(tree,0,sizeof(tree));
static int p[Max];
for(register int i=1;i<=n;i++) sum[i]=(double)num[i]-x;
for(register int i=1;i<=n;i++) sum[i]+=sum[i-1];
build(1,0,n);
for(register int i=L+1;i<=n;i++)
{
if(Q(1,0,n,max(i-R,0),i-L)<=sum[i]) return 1;
}
return 0;
}

int main()
{
//freopen("seq.in","r",stdin);
// freopen("seq.out","w",stdout);

n=get_int();
L=get_int();
R=get_int();
for(register int i=1;i<=n;i++) {num[i]=get_int();maxx=max(maxx,num[i]);}
l=0;
r=maxx;
for(register int i=1;i<=50;i++)
{
mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%0.4f\n",l);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: