链接: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 1≤t≤104,代表测试数据的组数
接下来t行每行一个数 N N N, 1 ≤ N ≤ 1 0 8 1 \le N \le 10^8 1≤N≤108
输出描述
对于每组输入,若能够在有限次内削减为0,在一行中输出重复的过程次数,否则输出 “Impossible”。
样例输入
3
1
2
3
样例输出
1
2
1
解题思路
BF做法,模拟每次依序减掉1,2,3,…直到不够减,记录次数,优化的方法是把前缀后记录,用二分查找直接完成一次过程,但这样还是会超时。
有一个讨巧的方法,存储已经得到答案的值,可以AC,测试数据有很多重复的值。
注意:不能在循环中使用已经存储的值,因为每一次重新加上的数 x x x的值不同。
#include
#include
#include
#include
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 t<i不断加上 N N N后重复,余数为 2 t 2t 2t, 3 t 3t 3t,… a t at at,当 a t > i at>i at>i时,减去 i i i后的余数 a t − i at-i at−i又是小于 i i i的,再次重复的余数为 ( a + 1 ) t − i (a+1)t-i (a+1)t−i,直到 b t − i > i bt-i>i bt−i>i,余数变为 b t − 2 i bt-2i bt−2i,重复下去,直到 c t − d i = i ct-di=i ct−di=i,即 c t = l c m ( t , i ) ct=lcm(t,i) ct=lcm(t,i),此时的 c c c即是重复过程的次数。
#include
#include
#include
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;
}