【數論】【搜索】【SCOI2009】遊戲
2012-02-09 20:31
141 查看
Description windy学会了一种游戏。对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应。最开始windy把数字按顺序1,2,3,……,N写一排在纸上。然后再在这一排下面写上它们对应的数字。然后又在新的一排下面写上它们对应的数字。如此反复,直到序列再次变为1,2,3,……,N。 如: 1 2 3 4 5 6 对应的关系为 1->2 2->3 3->1 4->5 5->4 6->6 windy的操作如下 1 2 3 4 5 6 2 3 1 5 4 6 3 1 2 4 5 6 1 2 3 5 4 6 2 3 1 4 5 6 3 1 2 5 4 6 1 2 3 4 5 6 这时,我们就有若干排1到N的排列,上例中有7排。现在windy想知道,对于所有可能的对应关系,有多少种可能的排数。 Input 包含一个整数,N。 Output 包含一个整数,可能的排数。 Sample Input 【输入样例一】 3 【输入样例二】 10 Sample Output 【输出样例一】 3 【输出样例二】 16 【数据规模和约定】 30%的数据,满足 1 <= N <= 10 。 100%的数据,满足 1 <= N <= 1000 。這是一道數論+記憶化搜索的題目。
遠問題可以轉化為一下問題:
已知a1+a2+a3+...+am = N,求Lcm(a1, a2, a3, ..., am)的所有可能性。
做法如下:枚舉從1到n之間的所有素數,再枚舉每個素數的方冪。由唯一分解定理可知,所枚舉的每一個不同的對象,都對應一個不同的解,再將這些解累加即可。
由於數據較大,必須使用記憶化搜索,並且要用64位整型。
Accode:
#include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> using std::cin; using std::cout; typedef long long int64; const int maxN = 1010; const int maxM = 210; int prime[210]; int64 f[210][1010]; int n; inline void mkprime(int Lim) { prime[0] = 2; prime[1] = 3; prime[2] = 5; int Last = 7; for (int i = 3; Last <= Lim; ++i, ++Last) for (; ; ++Last) { bool flag = true; for (int j = 0; prime[j] * prime[j] <= Last; ++j) if (Last % prime[j] == 0) { flag = false; break; } if (flag) { prime[i] = Last; break; } } } //製作一張素數表。 int64 Find(int i, int n) { if (prime[i] == -1 || prime[i] > n) //處理邊界。 return f[i] = 1; if (f[i] > -1) return f[i] ; //記憶化搜索。 f[i] = 0; for (int tmp = prime[i]; tmp <= n; tmp *= prime[i]) f[i] += Find(i + 1, n - tmp); //該素數被選,則枚舉它的次數。 return f[i] += Find(i + 1, n); //統計該素數未被選的情況。 } int main() { freopen("game.in", "r", stdin); freopen("game.out", "w", stdout); cin >> n; memset(f, 0xff, sizeof(f)); memset(prime, 0xff, sizeof(prime)); mkprime(n); cout << Find(0, n); return 0; }
第二次做,改进递推版,并且加上就地滚动,速度更快。
#include <fstream>
#include <cstring>
#include <cstdlib>
typedef long long int64;
const int maxP = 200;
const int maxN = 1010;
int64 f[maxN];
int prime[maxP];
int n, tot;
bool is_prime(int x)
{
for (int i = 0; prime[i]
* prime[i] <= x; ++i)
if (x % prime[i] == 0)
return false;
return true;
}
void mkprime()
{
prime[0] = 2;
for (int i = 3; i < n + 1; ++i)
if (is_prime(i))
prime[++tot] = i;
return;
}
int main()
{
std::ifstream cin("game.in");
cin >> n;
cin.close();
mkprime();
for (int j = 0; j < n + 1; ++j)
f[j] = 1;
for (int i = 0; i < tot + 1; ++i)
for (int j = n; j > -1; --j)
for (int k = prime[i];
k <= j; k *= prime[i])
f[j] += f[j - k];
std::ofstream cout("game.out");
cout << f
<< std::endl;
cout.close();
return 0;
}
相关文章推荐
- 【搜索】[SCOI2009] 生日快乐 BZOJ 1024
- BZOJ 1024 [SCOI2009]生日快乐 (搜索)
- BZOJ 1024 SCOI2009 生日快乐 暴力搜索
- 【BZOJ1024】[SCOI2009]生日快乐【搜索】
- 【bzoj1024】【scoi2009】【生日快乐】【搜索】
- [SCOI2009] 生日快乐(搜索)
- BZOJ 1025 [SCOI2009]游戏
- 【bzoj1853】【Scoi2010】【幸运数字】【搜索】
- BZOJ1297: [SCOI2009]迷路
- bzoj2393/bzoj1853 Cirno的完美算数教室/[Scoi2010]幸运数字 搜索+容斥
- bzoj 1026 [SCOI2009]windy数
- bzoj1293[SCOI2009]生日礼物 尺取法
- BZOJ 1026: [SCOI2009]windy数 数位DP
- bzoj 1026: [SCOI2009]windy数 (数位dp)
- 1024: [SCOI2009]生日快乐——爆搜
- BZOJ 2464: 中山市选[2009]小明的游戏 简单搜索
- bzoj1296【SCOI2009】粉刷匠
- bzoj1026: [SCOI2009]windy数
- BZOJ2393 & 1853 [Scoi2010]幸运数字 【搜索 + 容斥】
- [bzoj1297][SCOI2009]迷路