原题
Maths Problem Description
Android Vasya attends Maths classes. His group started to study the number theory recently. The teacher gave them several tasks as a homework. One of them is as follows.
There is an integer n. The problem is to find a sequence of integers a 1, …, a n such that for any k from 2 to n the sum a 1 + … + a k has exactly a k different positive divisors. Help Vasya to cope with this taskinput
The only line contains an integer n (2 ≤ n ≤ 100 000).
ouput
If there is no such sequence output “Impossible”. Otherwise output space-separated integers a 1, …, a n (1 ≤ a i ≤ 300).
Sample Input
3
Sample Output
1 3 4
题意分析:
题目的意思给出一个n,求一个n项的数列,其中ai>=1 && ai <= 300, 且对于任何 k>1 && k<=n,都有a1 + … + ak的因子数等于ak。
总体思路:
刚开始由于各种原因,我们无法完全理解题意,所以为了验证猜想(和题目意思一样)的正确性,决定先用回溯法,求出满足条件的式子,但输入n = 3后,求出了多个结果(比如1,2,2),导致我们停滞了很久。后来本着死马当活马的态度,强行做了一波。(最后是正确的,本题目有多个答案,并不要求和案例一样)
开头我们使用回溯法只是为了验证思路,因为这题的数据过大,所以回溯法显然会超时。
正确是思路应该是利用 “a1 + … + ak的因子数等于ak” 这个特性逆推,即已知当前前k项和为sum,求这个数列前k项的每一项(可以唯一确认此数列)。由于a(k)等于sum的因子数,就可以知道前k-1项和(即sum-a(k)),再由前k-1项和(sum-a(k))推出a(k-1),这样一步步逆推下去,如果最后可以求得sum == 0,就代表这个数字是可以划分的,如果sum是负数就不能划分。
思路清楚之后,我们就观察题目,发现n<=100000这个条件,也就是数列最大是100000项,那么我们只要找到一个数字x,同时x划分的数列有大于或等于100000项,之后根据输入的n,再输出前n项即可。
一:寻找x
思路:利用二分查找,找出一个划分的数列长度大于100000,并且尽可能接近100000的数字即可(为了节约资源),这样对于任何输入的n<=100000程序都可以保证有解。
代码:
#include <iostream>
#include <cmath>
using namespace std;
int f(int x){
int t = sqrt(x), i, sum = 0;
for (i = 1; i <= t; i++){
if (x%i == 0){
if (x/i == i){
sum += 1;
}else{
sum += 2;
}
}
}
return sum;
}
int main(){
int i, t, cnt, left = 2, right = 10000000;
while (1){
i = (left+right)/2;
t = i;
cnt = 0;
while (t > 0){
t -= f(t);
cnt++;
}
cout << i << ":" << cnt << endl;
if (cnt > 100000 && cnt-100000 < 500){
cout << "ans: " << i << endl;
break;
}
if (cnt >= 100000){
right = i;
}else{
left = i;
}
}
return 0;
}
//解为1589355,可分解为100339项
二:完整程序
思路:上面求解x的代码中,使用的求因子个数的函数f(int x)
过于暴力,所以最后一定会超时。所以我使用了另一种求因子数的方法,类似于筛法求素数!
AC代码
#include <cstdio>
#include <cmath>
#define MAXN 1589355
#define N 110010
using namespace std;
int a[MAXN+10] = {0};
void f(){
int i, j, t = sqrt(MAXN)+1;
for (i = 1; i <= t; i++){
for (j = i; i*j <= MAXN; j++){
a[i*j]++;
if (i != j) a[i*j]++;
}
}
}
int main(){
f();
int n, i, j;
int ans[N], nans = 0, t;
scanf ("%d", &n);
t = MAXN;
while (t > 0){
ans[nans++] = a[t];
t -= a[t];
}
for (i = nans-1, j = 0; j < n-1 && i > 0; j++, i--){
printf ("%d ", ans[i]);
}
printf ("%d", ans[i]);
return 0;
}