首页 > 代码库 > [BZOJ 1025] 游戏 置换群 背包DP

[BZOJ 1025] 游戏 置换群 背包DP

题意

  对于一个 $n$ 阶置换群 $A$ , 它的循环节大小分别为 $a_1, a_2, ..., a_m$ , 则有 $\sum_{i = 1} ^ m a_i = n$ .

  定义 $f(A)$ 为它的所有循环节的最小公倍数, 即 $f(A) = [a_1, a_2, ..., a_m]$ .

  求在所有 $n$ 阶置换群中, $f(A)$ 有多少种取值.

  $n \le 1000$ .

 

分析

  判断 $K$ 可不可取. $K = \prod_{i = 1} ^ r {s_r} ^ {t_r}$ 可取, 当且仅当最小的可取, 即 $\sum_{i = 1} ^ r {s_r} ^ {t_r} \le n$ .

  我们考虑预处理 $1$ 到 $n$ 所有的素数, 进行 背包DP .

  

实现

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>

#define F(i, a, b) for (register int i = (a); i <= (b); i++)

#define LL long long

const int N = 1005;

int n;
bool v[N]; int p[N], tot;
LL f[N][N], res;

int main(void) {
    #ifndef ONLINE_JUDGE
        freopen("bzoj1025.in", "r", stdin);
        freopen("bzoj1025.out", "w", stdout);
    #endif
    
    scanf("%d", &n);
    
    F(i, 2, n) v[i] = true;
    F(i, 2, n) {
        if (v[i]) p[++tot] = i;
        for (int j = 1; j <= tot && i * p[j] <= n; j++) {
            v[i * p[j]] = false;
            if (i % p[j] == 0) break;
        }
    }
    
    f[0][0] = 1;
    F(i, 1, tot)
        F(j, 0, n) {
            f[i][j] = f[i-1][j];
            for (int k = p[i]; j-k >= 0; k *= p[i])
                f[i][j] += f[i-1][j-k];
        }
    F(i, 0, n) res += f[tot][i];
    printf("%lld\n", res);
    
    return 0;
}

 

[BZOJ 1025] 游戏 置换群 背包DP