洛谷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[i][j]=\sum_{k=0}^{}dp[i-1][j-pow(p[i],k)] \]

最终答案就是一整行相加,不要忘记加上\(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;
}
posted @ 2022-01-04 20:31  文艺平衡树  阅读(31)  评论(0编辑  收藏  举报