題目:
官方題解:
循環節的長度爲各獨立置換環長度的最小公倍數。問題即求相加和爲N的正整數的最小公倍數的可能數。
由於1不影響最小公倍數,問題轉化爲相加小於等於N的若干正整數的最小公倍數的可能數。
如果這些正整數包含大於一個質因子,只會使得正整數的和更大。
因而問題再次轉化爲相加小於等於N的若干質數的最小公倍數的可能數。
N<1000,於是可遞推得,標程用記憶化搜索實現的。
個人理解:
可由N-1得到的最小公倍數,也一定可由N得到,因爲對N-1的任意劃分出的一列數,在這列數中添上一個1後,這列數的最小公倍數都不變。
於是對於N答案就是N-1的最小公倍數的數目加上N獨有的最小公倍數的數目。
解題的關鍵在於爲了得到N獨有的最小公倍數,必須把N化爲若干個質數的整數次冪的和,這些冪的最小公倍數就是N獨有的。
若把N化爲若干個數的和,且其中有合數,那麼使該合數的所有質因數替代該合數,最小公倍數並不變,但數列每項的和卻可能變小,而不可能變大,於是可能產生,N-1也可產生的最小公倍數。
此後問題變成,有多少種方法把N化成若干質數的整數次冪的和。這個問題的本質和有多少種方法將N化爲若干個正整數的和的本質是一樣的,dp解決。
標程:
#include <cstdio>
#include <cstring>
using namespace std;
typedef __int64 ll;
ll f[200][1024];
int p[200], g;
ll calc(int i, int n) {
if (i == g) return 1;
if (f[i][n] != -1) return f[i][n];
f[i][n] = calc(i + 1, n);
int cnt = p[i];
while (cnt <= n) {
f[i][n] += calc(i + 1, n - cnt);
cnt *= p[i];
}
return f[i][n];
}
int main() {
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
int n;
while (scanf("%d", &n) == 1) {
int i, j;
bool u[1024];
memset(u, 0, sizeof (u));
for (i = 2; i <= n; i++)
if (!u[i])
for (j = i + i; j <= n; j += i)
u[j] = true;
g = 0;
for (i = 2; i <= n; i++)
if (!u[i])
p[g++] = i;
memset(f, 0XFF, sizeof (f));
printf("%I64d\n", calc(0, n));
}
return 0;
}