P9748 [CSP-J 2023] 小苹果

题目描述

小 Y 的桌子上放着 n 个苹果从左到右排成一列,编号为从 1 到 n。

小苞是小 Y 的好朋友,每天她都会从中拿走一些苹果。

每天在拿的时候,小苞都是从左侧第 1 个苹果开始、每隔 2 个苹果拿走 1 个苹果。随后小苞会将剩下的苹果按原先的顺序重新排成一列。

小苞想知道,多少天能拿完所有的苹果,而编号为 n 的苹果是在第几天被拿走的?

输入格式

输入的第一行包含一个正整数 n,表示苹果的总数。

输出格式

输出一行包含两个正整数,两个整数之间由一个空格隔开,分别表示小苞拿走所有苹果所需的天数以及拿走编号为 n 的苹果是在第几天。

输入输出样例

输入 #1复制

8

输出 #1复制

5 5

说明/提示

【样例 11 解释】

小苞的桌上一共放了 8 个苹果。
小苞第一天拿走了编号为 1、4、7 的苹果。
小苞第二天拿走了编号为 2、6 的苹果。
小苞第三天拿走了编号为 3 的苹果。
小苞第四天拿走了编号为 5 的苹果。
小苞第五天拿走了编号为 8 的苹果。

【样例 22】

见选手目录下的 apple/apple2.in 与 apple/apple2.ans。

【数据范围】

对于所有测试数据有:1≤n≤10^9。

测试点n≤特殊性质
1∼210
3∼510^3
6∼710^6
8∼910^6
1010^9

特殊性质:小苞第一天就取走编号为 n 的苹果。

本蒟蒻看到这个第一个想到的是模拟,不会估时间复杂度,以为可以得到50分,实际90分。

看测试点10,数据范围n达到了10^9,最大布尔也只能开10^8,明显会因为数组下标越界RE。

先上90分代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define FOR(x,a,b,c) for(int x=a;x<=b;x+=c)
#define MFOR(x,a,b,c) for(int x=a;x>=b;x-=c)
#define MPFOR(x,a,b,c) for(int x=a;a<=b;x*=c)
const int N3=1e3+10;
const int N=1e6+10;
const long double esp=1e-8;
int gcd(int a,int b){
	int c=a%b;
	while(a%b!=0){
		a=b;
		b=c;
		c=a%b;
	}
	return b;
}
int lcm(int x,int y){
	return (x*y)/gcd(x,y);
}
ll ans1;//天数
ll ans2;//取到编号n苹果的天数
bool a[N];
int num;
bool f;
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	ll n;
	scanf("%lld",&n);
	ll nn=n;
	while(nn){
		num=0;
		ans1++;
		for(int i=1;i<=n;i++){
			if(num==0&&a[i]==0){//这一个是需要拿走的
				a[i]=1;
				if(i==n&&!f){//取到了编号n的苹果
					ans2=ans1;
					f=1;
				}
				nn--;
				num++;
			}
			if(a[i]==0){//不需要拿走但是还在
				num++;
			}
			if(num==3){//这一轮过完了,下一轮
				num=0;
			}
		}
	}
	printf("%lld %lld",ans1,ans2);
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

思路就是标记是否拿取,如果有那么跳过,没有进行判断等操作,时间复杂度本蒟蒻不会算。

接下来是满分思路

画个图

可以看到,这是样例第一组取出的苹果(打勾的)

再画一个

根据观察可以发现,如果想最后一个为当前这次拿走,必须满足n(个数)mod 3=1

所以,我们没有必要进行标记,就可以求出第二问了。

第一问更好求,每次减少的苹果是n÷3向上取整,n÷3为完整的组数,向上取整为剩下的不完整或者根本没有的一组。

上AC代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define FOR(x,a,b,c) for(int x=a;x<=b;x+=c)
#define MFOR(x,a,b,c) for(int x=a;x>=b;x-=c)
#define MPFOR(x,a,b,c) for(int x=a;a<=b;x*=c)
const int N3=1e3+10;
const int N=1e6+10;
const long double esp=1e-8;
int gcd(int a,int b){
	int c=a%b;
	while(a%b!=0){
		a=b;
		b=c;
		c=a%b;
	}
	return b;
}
int lcm(int x,int y){
	return (x*y)/gcd(x,y);
}
ll n;
ll ans1,ans2;//ans1记录天数,ans2记录拿走编号n苹果的天数
bool f=0;//标记是否拿走过编号n苹果
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	scanf("%lld",&n);
	while(n){
		++ans1;
		ll t=ceil(1.0*n/3);//减去的苹果
		if(n%3==1&&!f){//判断拿走的最后一个是不是编号为n
			ans2=ans1;
			f=1;
		}
		n-=t;//拿走这些苹果
	}
	printf("%lld %lld",ans1,ans2);
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

注意,答案开不开long long无所谓,且ll n之前那一大堆函数没用

有任何错误请在评论区指出,作者将给予修改。

普及组T1差点不会做……

  • 10
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值