初見安~這裏是傳送門:洛谷P4161 [SCOI2009] 遊戲
題解
可以發現的一件事情是,這就是一個置換的題目【廢話】每寫一行,就相當於是進行了一次自身的置換操作。很明顯的是,一個置換進行多次操作後總能回到原本的樣子,總是循環的,而我們要求的就是可能有多少種循環的長度。根據置換與輪換的知識,我們可以把任意一個置換寫成輪換的形式,例如:
,等式右邊就是輪換形式,表示這個置換是這兩個循環組合起來的。也就是說,每次操作就相當於是各個循環內部進行一次平移,並且週期就是循環的長度。所以我們要求的整體的循環長度就是各個小循環的長度的最小公倍數。所以問題就轉化爲:將n表示成,求有多少種ans滿足。
現在我們就可以單獨考慮lcm的問題了。因爲,所以爲了方便我們找lcm,我們用n以內的質數來湊,就不用考慮gcd了。換言之, 因爲有,其中提供這些質因數的循環的長度的和滿足【爲什麼是小於,因爲你讓剩下的小循環長度都爲1就不影響lcm了】,所以我們可以用dp來計數。
設表示各個的和,也就是對lcm的大小做了貢獻的 長度爲指數的冪次的 小循環的總長度。我們可以預處理出n以內的質數
,枚舉冪次,dp即可。
最後的答案就是。
上代碼:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 1005
using namespace std;
typedef long long ll;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
int n;
int pri[maxn], tot = 0;
ll f[maxn];
bool vis[maxn];
signed main() {
n = read();
for(int i = 2; i <= n; i++) {//篩質數
if(!vis[i]) pri[++tot] = i, vis[i] = true;
for(int j = 1; j <= tot && i * pri[j] <= n; j++) vis[i * pri[j]] = true;
}
f[0] = 1;//分解爲了n個小循環的情況
for(int i = 1; i <= tot; i++) for(int j = n; j >= pri[i]; j--) {
register int tmp = pri[i];//計數,應該還是好理解的
while(tmp <= j) f[j] += f[j - tmp], tmp *= pri[i];
}
for(int i = 1; i <= n; i++) f[0] += f[i];//直接用f[0]計數,開個ans也行
printf("%lld\n", f[0]);
return 0;
}