BZOJ1025 [SCOI2009]游戏

Description

  windy学会了一种游戏。对于1到N这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想知道,对于所有可能的对应关系,有多少种可
能的排数。

Input

  包含一个整数N,1 <= N <= 1000

Output

  包含一个整数,可能的排数。

Sample Input

【输入样例一】
3
【输入样例二】
10

Sample Output

【输出样例一】
3
【输出样例二】
16

题解

首先,我们考虑如果有一个置换(即对应关系),我们把它写成循环,例如题目描述中可以写成

$$(1, 2, 3)(4, 5)(6)$$

即每个括号内循环对应。那么,显而易见的,排数等于其第一次回到初始位置的步数加一,即各循环长度的最小公倍数乘积加一。

那么,题目就转化为:将N分解成一些数,每一个分解求出最小公倍数,求有多少不相同的最小公倍数。

由于将一个数分为互质的两个数会更优(即最小公倍数不会改变且和不会更大),所以我们只需要考虑置换中只有质数的幂和1。

那么,问题就转换成了:将N分解成质数幂和1的和,且各质数最多只出现一个幂,有多少种分法?

这个问题就是裸的分组背包,即每一组物品是$(p, p^2, ..., p^{\lfloor log_pn\rfloor})$

附代码:

#include <cstdio>
typedef long long LL;
const int N = 1050;
bool mark[N];
int cnt, prime[500];
void getPrime(int n) {
  cnt = 0;
  for (int i = 2; i <= n; ++i) {
    if (!mark[i])
      prime[cnt++] = i;
    for (int j = 0; j < cnt && prime[j] * i <= n; ++j) {
      mark[i * prime[j]] = 1;
      if (!(i % prime[j])) break;
    }
  }
}
LL f[N];
LL tmp[N];
int main() {
  int n;
  scanf("%d", &n);
  f[0] = 1;
  getPrime(n);
  for (int i = 0; i < cnt; ++i) {
    for (int j = 0; j <= n; ++j) {
      tmp[j] = 0;
      for (int k = prime[i]; k <= j; k *= prime[i])
        tmp[j] += f[j - k];
    }
    for (int i = 0; i <= n; ++i)
      f[i] += tmp[i];
  }
  for (int i = 0; i < n; ++i)
    f[n] += f[i];
  printf("%lld\n", f[n]);
  return 0;
}

  

posted @ 2017-06-11 10:23  _rqy  阅读(340)  评论(0编辑  收藏  举报