Maths

原题

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 task

input

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值