洛谷P4161 [SCOI2009]游戏
题目
https://www.luogu.com.cn/problem/P4161
思路
简化一下题意就是给定\(N\),这\(N\)个元素可构造 \(N!\) 种不同的置换,求这些置换总共有多少种不同的阶。
开始的一些想法:
显然的,任意一个置换都可以分解为若干互不相交的循环的乘积,而置换的阶,就是这些循环的阶的最小公倍数。(不加证明,组合数学书上有)
用数学语言描述一下:\(N=a_1+a_2+a_3+...+a_k\),求\(lcm(a_1,a_2,a_3....a_k)\) 的取值种数。
一个很naive的想法:我们珂以枚举每一个数看看它是否有可能成为符合题意的lcm,具体操作就是利用整数的唯一分解,把它分解成\(p_1^{k_1}*p_2^{k_2}*p_3^{k_3}...\) (\(p_i\geq 1\))这种形式。
那 \(lcm\) 是这个数的情况下,给定的 \(N\) 能否分解出符合条件的 \(a_1+a_2+a_3+...+a_k\) 呢?
我们必然是要让所有 \(a_i\) 的总和尽可能小(如果总和比 \(N\) 还小,可以添加若干项 \(1\) 来解决),不难发现,最优解必然是\(a_i=p_i^{k_i}\)(也就是每个\(a\)都只取一个质因子,而且取到最高次幂)。
正确性是显然的,因为 \(p_1^m*p_2^n=lcm(p_1^m,p_2^n)\) 且 \(p_1^m+p_2^n < p_1^m*p_2^n\),所以 \(a\) 只含一个质因子,又因为如果不取到最高次幂显然对最终的\(lcm\)无贡献,综上可得。
只要总和小于等于 \(N\),那么当前枚举的 \(lcm\) 就可行了,由此我们得到了一个正确性有保障的解法。然而,可惜的是,合法的\(lcm\)可以远远大于N,这样检验的方法就不可行了。
优化
尽管上述的方法并不可行,但是结论依旧是有用的,至少我们得知 \(a\) 必定是\(p_i^{k_i}\)这样的形式。那我们就珂以把 \(N\) 以内的质数全拎出来,对每一个\(p_i\)枚举它的指数,看看在给定\(N\)的限制下有多少种方案。
等等,这不就是一个背包嘛(`・ω・´),再看下数据范围\(N\leq 1000\),大概率是正解。
我们用 \(dp[i][j]\) 表示在考虑前 \(i\) 个质因子,所有 \(a\) 的总和等于 \(j\) 时的方案数。
那转移方程就是
最终答案就是一整行相加,不要忘记加上\(dp[N][0]\)表示恒等置换的情况。
可以用滚动数组进行空间优化。
代码
#include<cstdio>
#include<cstdlib>
#define maxn 1003
#define ll long long
using namespace std;
int N;
ll dp[2][maxn];
int p[maxn],book[maxn],cnt=0;
int main(){
int i,j,k,m;
ll ans=0;
scanf("%d",&N);
book[1]=1;
for(i=2;i<maxn;++i){
if(!book[i]) p[++cnt]=i;
for(j=1;j<=cnt&&i*p[j]<maxn;++j){
book[i*p[j]]=1;
if(!(i%p[j])) break;
}
}
dp[0][0]=1;
for(i=1;p[i]<=N&&i<=cnt;++i){
for(j=0;j<=N;++j){
dp[i&1][j]=dp[!(i&1)][j];
for(k=p[i];k<=j;k*=p[i]){
dp[i&1][j]+=dp[!(i&1)][j-k];
}
}
}
m=i-1;
for(i=0;i<=N;++i) ans+=dp[m&1][i];
printf("%lld",ans);
// system("pause");
return 0;
}