題目
windy學會了一種遊戲。
對於1到N (N<=1e3) 這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想知道,對於所有可能的對應關係,有多少種可能的排數。
思路來源
https://www.luogu.com.cn/problemnew/solution/P4161
題解
題面看似很長,實際上就是輪換麼,i每次到pi,
注意到,一個長度爲x的輪換需要x次才能轉回去,
那整體局面的循環節就是子局面循環節的最小公倍數,
問題等價於把n分成若干個整數x1,...,xm,使得m個數之和爲n,且排數=lcm(x1,...,xm)
對於每個合法的lcm,都存在一個最小的表示和sum,sum<=n,
n能表示的方案都能用其對應sum表示的方案代替,
只需要找到最小的sum,剩下的都用1補齊即可,這樣排數就可以被唯一計數了
那爲了使sum最小,一個lcm應由它所有質因子來表示,纔沒有因共同的gcd而造成的損失
故一個和值,應該對應質因子之和的方案,且爲了使2 3 5 7和2 3 7 5不被重複計算(可重集計數),
應該把相同的質因子放在一起,枚舉這種質因子填幾個,然後就變成完全揹包問題了
考慮f[i][j]+=f[i-1][j-pr[i]的k次冪,則顯然倒序滾掉這一維,
最後轉化過來之後的實現不難,然而還是比較思維一點吧
代碼
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
const int N=1e3+10;
typedef long long ll;
int n,pr[N],d[N],cnt;
ll dp[N];
int main(){
scanf("%d",&n);
rep(i,2,n){
if(!d[i]){
d[i]=i;
pr[cnt++]=i;
}
for(int j=0,k;(k=pr[j]*i)<=n;++j){
d[k]=pr[j];
if(d[i]==pr[j])break;
}
}
dp[0]=1;
rep(i,0,cnt-1){
per(j,n,1){
for(int k=pr[i];k<=j;k*=pr[i]){
dp[j]+=dp[j-k];
}
}
}
rep(i,1,n)dp[i]+=dp[i-1];
printf("%lld\n",dp[n]);
return 0;
}