P8918 『MdOI R5』Jump 题解

面向样例编程:奇偶数跳跃问题的解法
文章讨论了一道编程题,涉及判断在给定条件下能否通过特定跳跃方式到达一个整数,以及当能到达时的最小步数。关键点是奇偶性分析和利用对数性质得出答案。

本篇题解为许多赛时面向样例编程的人证明结论的正确性

题目大意

给出 ttt 组测试数据,对于每组测试数据给出一个正整数 nnn,表示期望跳 ansansans 步可以到达 nnn,每步可以跳 2i−12 ^ {i-1}2i1 个单位,其中 1≤i≤ans1 \le i \le ans1ians。输出 ansansans 的最小值。如果无论如何都不能到达 nnn,则输出 −1-11

结论

赛时观察样例,发现如果 n≡0(mod2)n\equiv0\pmod{2}n0(mod2),即 nnn 为偶数时,无法到达 nnn,答案为 −1-11。如果 n≡1(mod2)n\equiv1\pmod{2}n1(mod2),即 nnn 为奇数,答案为 ⌈log⁡2n⌉\lceil \log _ 2 n \rceillog2n

证明

证明分两部分。第一部分证明答案为 −1-11 时的正确性,第二部分证明如果有答案,如何求出正确答案。

无法到达

这个部分比较简单。

按照题意,第一步走了 20=12^0=120=1 个单位长度,从第二步开始,每次走的距离为偶数。一个数加减一个偶数奇偶性不变,所以走到的地方必为奇数,故得证。

求出答案

这个部分较为复杂。

首先给出一个不必证明的结论(这个很基础,限于篇幅我不给出证明),后文简称结论:给定一个集合 {1,2,4,8,…,2n∣n∈N+}\{1,2,4,8,\ldots,2^n | n \in N_+ \}{1,2,4,8,,2nnN+},可以任意选取这个集合的某个非空子集 BBB,对 BBB 的每个元素求和,记这个和为 sumsumsum。规定一个新的集合 CCC,其包含所有可能的 sumsumsum 的值,则 C={1,2,3,…,2n+1−1}C = \{ 1,2,3,\ldots,2^{n+1}-1 \}C={1,2,3,,2n+11}

然后发现结论和这道题相似度非常大,区别在于:结论中对于每一个元素可选可不选,而本题对于每个元素必须选但可以求差。

类比结论,我们发现:如果一个数加上 2x2^x2x,则相当于加上 2x+1−2x2 ^ {x+1} - 2 ^ x2x+12x,也就是说如果 2m2 ^ m2m 在结论中没有被选中,而在本题中必须被选中,我们可以借助 2m−12 ^ {m-1}2m1 把它抵消掉,相当于没有选。那如果 2m−12 ^ {m-1}2m1 也没有选呢?考虑类似递归的选法,去选 2m−22 ^ {m-2}2m2,如果满足就选上,如果不满足就继续递归。虽然结论中有不选 111 的极端情况会出现没有递归出口,但是考虑到这道题 n≡0(mod2)n\equiv0\pmod{2}n0(mod2) 的情况是没有解的,所以所有情况成立,故得证。

所以答案为 ⌈log⁡2n⌉\lceil \log _ 2 n \rceillog2n

代码

#include<bits/stdc++.h>
using namespace std;
#define Getchar() p1==p2 and (p2=(p1=Inf)+fread(Inf,1,1<<21,stdin),p1==p2)?EOF:*p1++
#define Putchar(c) p3==p4 and (fwrite(Ouf,1,1<<21,stdout),p3=Ouf),*p3++=c
char Inf[1<<21],Ouf[1<<21],*p1,*p2,*p3=Ouf,*p4=Ouf+(1<<21);
inline void read(int &x,char c=Getchar())
{
	bool f=c!='-';
	x=0;
	while(c<48 or c>57) c=Getchar(),f&=c!='-';
	while(c>=48 and c<=57) x=(x<<3)+(x<<1)+(c^48),c=Getchar();
	x=f?x:-x;
}
inline void write(int x)
{
	if(x<0) Putchar('-'),x=-x;
	if(x>=10) write(x/10),x%=10;
	Putchar(x^48);
}
int t,n;
int main()
{
	read(t);
	while(t--)
	{
		read(n);
		if(n==1) Putchar('1'),Putchar('\n');
		else if(n%2==0) Putchar('-'),Putchar('1'),Putchar('\n');
		else write((ceil)(log2(n))),Putchar('\n');
	}
	fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值