Project Euler 303

正の整数 n に対し, f(n) を, n の倍数であり 10 進数で表すと 2 以下の数字のみが用いられる最小の数と定義する.

ゆえに, f(2)=2, f(3)=12, f(7)=21, f(42)=210, f(89)=1121222 である.

また,  \sum^{100}_{n=1} \frac{f(n)}{n} = 11363107 である.

 \sum^{10000}_{n=1} \frac{f(n)}{n} を求めよ.

#pragma comment(lib,"mpir.lib")
#include <iostream>
#include <deque>
#include <set>
#include <mpir.h>
#include <string>
using namespace std;

string numarr[10001];
void insertNumarr(string str, int n);

// str -> f(n)
// 1<= n <= 10000
void insertNumarr(string str, int n){
	for(int i=1 ; n*i <= 10000 ; i++){
		numarr[n*i] = str;
	}	
}

string mpzFind303(int n){
	bool f;
	mpz_t i, k, tmp;

	mpz_init_set_str(i, numarr[n].c_str(), 3);

	if(n==9999) mpz_set_str(i, "11112222222222222222", 3);

	mpz_sub_ui(i, i, 1);

	if(mpz_cmp_si(i,0)<0) mpz_set_ui(i,0);

	mpz_init(k);
	mpz_init(tmp);

	char c[100000];
	
	while(1){
		//i++;
		mpz_add_ui(i,i,1);
		f = true;

		mpz_set(k,i);
		mpz_get_str(c, 3, k);
		mpz_set_str(k, c, 10);
		mpz_mod_ui(tmp, k, n);

		if(mpz_get_ui(tmp)==0){
			char chr[10000]="";
			string str;
			mpz_get_str(chr, 10, k);
			str = string(chr);
			insertNumarr(str, n);

			return str;
		}
	}
}

int main(){
	
	string str;
	char chr[10000];

	numarr[0]="1";
	numarr[1]="1";

	mpz_t sum, f;
	mpz_init_set_ui(sum, 0);
	mpz_init(f);

	for(int i=1 ; i<=10000 ; i++){
		str = mpzFind303(i);
		cout << i << " " << str << endl;

		//charの配列にstringを入れる
		strcpy_s(chr, str.c_str());

		//f = chr;
		mpz_set_str(f, chr, 10);
		mpz_div_ui(f, f, i);

		mpz_add(sum, sum, f);
	}
	
	cout << endl;
	mpz_out_str(stdout, 10, sum);
	cout << endl;
}

@phyllo にそそのかされてすごく後の方の問題をがんばってみた。
紆余曲折あってこの形になりました。f(9999)だけ異常にでかいので手計算。
解説は気が向いたらやります・・・


Project Eulerはforumが見れるんですが、そこに書いてあるコードの速さといったらないです。あとで読んで勉強しないと・・・