1 条题解
-
0
自动搬运
来自洛谷,原作者为

OtterZ
刷题时间到!搬运于
2025-08-24 23:00:51,当前版本为作者最后更新于2024-11-21 14:04:02,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目
求有多少个全排列,使 。
要求长度为 。
引理
引理:每一个排列从一个位置开始环形遍历,可得到结尾分别为 的单调下降序列拼合得到的全排列。
证:对于 ,有 ,故而 时总有 ,而 ,有 ,由此可得,每一个排列从一个位置开始环形遍历,可得到结尾分别为 的单调下降序列拼合得到的全排列,证毕。
动规
对于其中一个下降子序列,每个数前可接:
- ;
- ;
- 。
我们设 为两序列结尾较小值为 ,较大值为 的对应排列数,根据 的插入情况可得到转移方程。
优化
再来一个引理
引理:设 $s_i = \{x|j \in N^+,x \bmod i \le 2 \text{且} x > i + 1\}$,有 。
证:对于一个序列结尾为 ,另一个序列结尾为 前为 时, 此时在另一个序列上,即 前,此时得到一个合法结果,还有一个情况,即将所有 放在另一个序列中也会得到一个合法解。

证毕。
基于引理的快速动规
我们要的答案为 ,根据引理得到的公式可 得出,这样就解决了问题。
注意 的特殊情况和转移方程的范围判断与去重。
代码
#include<cstdio> using namespace std; int n,m,dp[1000009]; int ans; #define mod 1000000007 int main(){ scanf("%d",&n); if(n == 1){ puts("1"); return 0; } dp[n - 1] = 1; ans = 1; for(int i = n - 2; i > 0; i --){ dp[i] = 1; for(int j = 1; j * i <= n; j ++){ if(j * i > i + 1){ //(i,i + 1) -> (j * i - 1,j * i) dp[i] = (dp[i] + dp[i * j - 1]) % mod; } if(j * i > i && j * i + 1 <= n && i > 1){//(i,i + 1) -> (j * i,j * i + 1) dp[i] = (dp[i] + dp[i * j]) % mod; } if(j * i + 1 > i && j * i + 2 <= n && i > 2){//(i,i + 1) -> (j * i + 1,j * i + 2) dp[i] = (dp[i] + dp[i * j + 1]) % mod; } } // printf("%d\n",dp[i]); } printf("%d\n",1ll * dp[1] * n % mod); }
- 1
信息
- ID
- 9339
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者