消减整数(数论)

链接:https://ac.nowcoder.com/acm/contest/10746/F
来源:牛客网

题目描述
给你一个数字N依序减掉1,2,3,…直到不够减。如果刚好减到0就结束,否则就加上N继续从1开始减,直到减到0为止。
请给出一个数字,计算对于该数字一共重复了几次这个过程。

输入描述
输入的第一行有一个正整数 t t t 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104,代表测试数据的组数
接下来t行每行一个数 N N N 1 ≤ N ≤ 1 0 8 1 \le N \le 10^8 1N108

输出描述
对于每组输入,若能够在有限次内削减为0,在一行中输出重复的过程次数,否则输出 “Impossible”。

样例输入
3
1
2
3

样例输出
1
2
1

解题思路
BF做法,模拟每次依序减掉1,2,3,…直到不够减,记录次数,优化的方法是把前缀后记录,用二分查找直接完成一次过程,但这样还是会超时。
有一个讨巧的方法,存储已经得到答案的值,可以AC,测试数据有很多重复的值。
注意:不能在循环中使用已经存储的值,因为每一次重新加上的数 x x x的值不同。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
int T, n;
const int N = 1e5;
int a[N], cnt;
void init() {
	int i = 1, sum = 0;
	while (sum < 1e8) {
		sum += i;
		a[i] = sum;
		i++;
	}
	cnt = i;
}
map<int, int> mp;
void solve(int x) {
	if (mp.count(x)) {
		printf("%d\n", mp[x]);
		return;
	}
	int t = x, res = 0;
	while (1) {
		res++;
		int i = lower_bound(a+1, a+cnt, t) - a;
		if (t == a[i]) {
			mp[x] = res;
			printf("%d\n", res);
			break;
		}
		t -= a[i-1];
		t += x;
	}
}
int main() {
	//freopen("消减整数.in", "r", stdin);
	scanf("%d", &T);
	init();
	while (T--) {
		scanf("%d", &n);
		solve(n);
	}
	return 0;
}

正解,假设 N N N不够减后余数是 t t t,下一个要减的数是 i i i t < i t<i ti不断加上 N N N后重复,余数为 2 t 2t 2t, 3 t 3t 3t,… a t at at,当 a t > i at>i ati时,减去 i i i后的余数 a t − i at-i ati又是小于 i i i的,再次重复的余数为 ( a + 1 ) t − i (a+1)t-i (a+1)ti,直到 b t − i > i bt-i>i btii,余数变为 b t − 2 i bt-2i bt2i,重复下去,直到 c t − d i = i ct-di=i ctdi=i,即 c t = l c m ( t , i ) ct=lcm(t,i) ct=lcm(t,i),此时的 c c c即是重复过程的次数。

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int T, n;
const int N = 1e5;
int a[N], cnt;
void init() {
	int i = 1, sum = 0;
	while (sum < 1e8) {
		sum += i;
		a[i] = sum;
		i++;
	}
	cnt = i;
}
int gcd(int a, int b) {
	if (b == 0) return a;
	return gcd(b, a%b);
}
void solve(int x) {
	int i = lower_bound(a+1, a+cnt, x) - a;
	int t = x - a[i-1];
	int res = i * t / gcd(i, t);
	printf("%d\n", res/t);
}
int main() {
	//freopen("消减整数.in", "r", stdin);
	scanf("%d", &T);
	init();
	while (T--) {
		scanf("%d", &n);
		solve(n);
	}
	return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值