 /**//*
给定一个n < 2^31
在[1,n]中选出一些数,每个数可以选多个, 但要求他们的和为n
而且只用这些数能唯一表示[1,n]中所有的数
如n = 5, 有{1,1,1,1,1} {1,2,2} , {1,1,3}

看到n这么大,应该是数学之类的方法或者logn,sqrt(n)之类的
想不出怎么缩小规模 -_,-

看这里的
http://knol.google.com/k/wenlei-xie/acm-icpc-dhaka-2007-%E8%A7%A3%E9%A2%98%E6%8A%A5%E5%91%8A/15moho0gp59j7/3#

首先必须有1,然后枚举包含k个1,则能表示[1,k],则接下来的数就是k+1
如果有1个k+1,则[1,2k+1]都能被表示了,所以下一个数只能是2(k+1)
如果有2个k+1,则[1,3k+2]都能被表示了,所以下一个数只能是3(k+1)

但无论如何,接下来的数只能是t(k+1)
所以这个集合,除了k个1之外,其他数都是t(k+1) , t >=1
由于需要和为n,所以k+1 | n-k
所以答案为:f(n) = ∑f((n-k)/(k+1)) = ∑f((n+1)/(k+1)-1) k>=1, k+1 | n-k
边界为f(0) = 1
因此可以枚举n+1的因子,sqrt(n+1)的复杂度,有点慢
用个map记录下结果

但解题报告那里是对n分解质因子为∏pi^ai,用这种方法去枚举因子
厄。。。还没试过
*/
#include<iostream>
#include<cstring>
#include<map>
#include<algorithm>
#include<stack>
#include<queue>
#include<cmath>
#include<string>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<set>
#include<list>
#include<numeric>
#include<cassert>
#include<ctime>

using namespace std;

map<unsigned int , long long> mp;

 long long solve(unsigned int n) {
map<unsigned int , long long>::iterator it = mp.find(n);
 if (it != mp.end()) {
return it->second;
}
long long ans = 0;
 for (unsigned int k = 1; k+1 <= (n+1)/(k+1) ; k++) {
 if ((n+1) % (k+1) == 0) {
ans += solve((n+1)/(k+1) - 1);
unsigned int kk = (n+1)/(k+1);
 if (kk != k+1) {
ans += solve(k);//k+1-1
}
}
}
return mp[n] = ans + 1;
}

int main()
  {
#ifndef ONLINE_JUDGE
//freopen("in","r",stdin);
#endif

mp[0] = 1;
int T, t = 1;
 for (scanf("%d", &T); T-- ;) {
unsigned int n;
scanf("%u", &n);
printf("Case %d: %lld\n", t++, solve(n));
}
return 0;
}
|
|
常用链接
随笔分类
Links
搜索
最新评论

|
|