【NOI Online #2】endless

Update 5/13

不敢相信我竟然爆零了。发现题解代码有误,那我就给个没错的代码吧

#include<cstdio>//O(nlogn)
#include<algorithm>
using namespace std;
long long a[100000];
long long length[100000];
bool cmp(long long x,long long y){return x>y;}
int main()
{
//	freopen("endless.in","r",stdin);
//	freopen("endless.out","w",stdout);
	long long n,l,v,q;
	scanf("%lld %lld %lld",&n,&l,&v);
	//printf("n=%d,l=%d,v=%d",n,l,v);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		//printf("a[%d]=%d\n",i,a[i]);
	}
	scanf("%lld",&q);
	//printf("q=%d\n",q);
	sort(a+1,a+1+n,cmp);
	length[0]=l;
	//printf("max_use_%d_magic_l=%d\n",0,t[0]);
	for(int i=1;i<=n;i++)
	{
		length[i]=length[i-1]+a[i];
		//printf("max_use_%d_magic_l=%d\n",i,t[i]);
	}
	for(int i=1;i<=q;i++)
	{
		long long tx;
		scanf("%lld",&tx);
		tx*=v;
		bool have=false;
		long long xyx=upper_bound(length,length+1+n,tx)-length;
		if(xyx>n)printf("-1\n");
		else printf("%lld\n",xyx);
	}
	return 0;
}

注意!!这里最终给的代码是错误的

这道题就是一个人,往上走,有一种魔法可以让这个人在走到ai的高度时下来。

1.0版本:O(n*n!)

首先最容易想到的办法就是直接暴力搜索,每一个魔法都有选和不选的两种方案。那我们就可以打一个O(n*n!)复杂度的代码。

然而这种算法太慢了,n=十几就爆炸了…

代码就不贴了…

什么??你非得让我贴??好吧,告诉你,我不会.

2.0版本:O(n*n)

考虑第一个subtask或者什么**东西

我们可以算出不用魔法和用的时间,然后判断一下,伪代码如下:

var x=不用魔法的时间
var x2=用魔法的时间
t<x:0
x<t<x2:1
else -1

那么我们想想可不可以把这种方法推广。

先看一组样例:设宙斯有5种魔法,西西弗斯的速度为2,山的高度为10,作用地点值分别为
1   5   3   4   2 1 \space 5 \space 3 \space 4 \space 2 1 5 3 4 2
那么想想看,如果宙斯用一个膜法,最多能够使得他的多走多少时间呢?(作者提示:这是一个用脚都能想出来的问题)
如果宙斯选择最大的一个,就能使他走的时间变得更多

那如果选两个呢??当然是选第一大的和第二大的啦。

因为这题没有让我们输出都用了哪些魔法如果让我们输出那我就不会了所以可以在输入a了之后,建一个预处理数组time,表示使用n个魔法时西西弗斯的最大上山时间(注意要用double),然后用for循环判断哪一个时间刚好大于宙斯询问的时间。如果询问的时间比time[n]还要大,那么输出-1

code:

#include<cstdio>//O(n^2)
#include<algorithm>
using namespace std;
int a[100000];
double time[100000];
bool cmp(long long x,long long y){return x>y;}
int main()
{
//	freopen("endless.in","r",stdin);
//	freopen("endless.out","w",stdout);
	int n,l,v,q;
	scanf("%d %d %d",&n,&l,&v);
	//printf("n=%d,l=%d,v=%d\n",n,l,v);
	double T=l*1.0/v;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		//printf("a[%d]=%d\n",i,a[i]);
	}
	scanf("%d",&q);
	//printf("q=%d\n",q);
	sort(a+1,a+1+n,cmp);
	time[0]=T;
	//printf("max_use_%d_magic_time=%f\n",0,time[0]);
	for(int i=1;i<=n;i++)
	{
		time[i]=time[i-1]+a[i]*1.0/v;
		//printf("max_use_%d_magic_time=%f\n",i,time[i]);
	}
	for(int i=1;i<=q;i++)
	{
		int tx;
		scanf("%d",&tx);
		bool have=false;
		for(int j=0;j<=n;j++)
		{
			if(time[j]>tx)
			{
				printf("%d\n",j);
				have=true;
				break;
			}
		}
		if(!have)printf("-1\n");
	}
	return 0;
}

3.0版本:O(nlogn)

观察O(n*n)的代码,我们会发现time数组满足一个规律:
如 果 x > y , 则 t i m e [ x ] > t i m e [ y ] 如果x>y,则time[x]>time[y] x>y,time[x]>time[y]

那么,这个序列就是个不下降的序列。那么,你想起了什么?

对了,就是二分查找。我们可以使用stl中的upper_bound函数。这个函数可以返回序列中的第一个大于x的数。刚好满足我们的要求。优化后的代码如下:

#include<cstdio>//O(nlogn)
#include<algorithm>
using namespace std;
int a[100000];
double time[100000];
bool cmp(long long x,long long y){return x>y;}
int main()
{
//	freopen("endless.in","r",stdin);
//	freopen("endless.out","w",stdout);
	int n,l,v,q;
	scanf("%d %d %d",&n,&l,&v);
	printf("n=%d,l=%d,v=%d\n",n,l,v);
	double T=l*1.0/v;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		//printf("a[%d]=%d\n",i,a[i]);
	}
	scanf("%d",&q);
	//printf("q=%d\n",q);
	sort(a+1,a+1+n,cmp);
	time[0]=T;
	//printf("max_use_%d_magic_time=%f\n",0,time[0]);
	for(int i=1;i<=n;i++)
	{
		time[i]=time[i-1]+a[i]*1.0/v;
		//printf("max_use_%d_magic_time=%f\n",i,time[i]);
	}
	for(int i=1;i<=q;i++)
	{
		int tx;
		scanf("%d",&tx);
		long long xyx=upper_bound(time,time+1+n,tx)-time;
		if(xyx>n)printf("-1\n");
		else printf("%lld\n",xyx);
	}
	return 0;
}

3.1版本 精度优化

因为如果每次都去算时间可能会有浮点数的精度误差,所以我们可以用路程替换时间

比如宙斯想要使得时间大于5,西西弗斯的速度是2,那么就相当于求使得西西弗斯的路程大于10.

高度为ai的魔法其实就是让他的路程增加ai,这里让大家想一想为啥。

code(我知道你们就看这里):

#include<cstdio>//O(nlogn)
#include<algorithm>
using namespace std;
long long a[100000];
long long length[100000];
bool cmp(long long x,long long y){return x>y;}
int main()
{
//	freopen("endless.in","r",stdin);
//	freopen("endless.out","w",stdout);
	long long n,l,v,q;
	scanf("%lld %lld %lld",&n,&l,&v);
	//printf("n=%d,l=%d,v=%d",n,l,v);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		//printf("a[%d]=%d\n",i,a[i]);
	}
	scanf("%d",&q);
	//printf("q=%d\n",q);
	sort(a+1,a+1+n,cmp);
	length[0]=l;
	//printf("max_use_%d_magic_l=%d\n",0,t[0]);
	for(int i=1;i<=n;i++)
	{
		length[i]=length[i-1]+a[i];
		//printf("max_use_%d_magic_l=%d\n",i,t[i]);
	}
	for(int i=1;i<=q;i++)
	{
		long long tx;
		scanf("%lld",&tx);
		tx*=v;
		bool have=false;
		long long xyx=upper_bound(length,length+1+n,tx)-length;
		if(xyx>n)printf("-1\n");
		else printf("%lld\n",xyx);
	}
	return 0;
}

谢谢观看!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值