Address
Meaning
称一个
的排列
的完美数为:有多少个
满足
。
求有多少个长度为
的完美数恰好为
的排列。
Solution
考虑容斥来做。具体地,设
表示在
中选出
个数,让它们中的每一个数
都满足
,剩下的
个数随便排的方案数。
那么答案为:
考虑通过 dp 求
。
表示在
内选出
个数放进
里面(满足与自己的位置差的绝对值为
),最后三维表示
,
,
三个位置是否已经有数填入。特别地,如果位置不存在则这一维只能为
。
边界
。
转移(1):
不被放进
内(作为剩下来的数随便排)。
转移(2):如果
且
位置还没有数则可以把
放在
。
转移(3):如果
且
位置还没有数则可以把
放在
。
如果在
个数中选出
个数使得其中每个数
都满足
,则在计算
时剩下
个随便排。
复杂度
。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
const int N = 1005, ZZQ = 1e9 + 7;
int n, m, fac[N], C[N][N], f[N][N][2][2][2], ans;
int main()
{
int i, j, op1, op2, op3;
cin >> n >> m;
fac[0] = 1;
For (i, 1, n) fac[i] = 1ll * fac[i - 1] * i % ZZQ;
For (i, 0, n) C[i][0] = 1;
For (i, 1, n) For (j, 1, i)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % ZZQ;
f[0][0][0][0][0] = 1;
For (i, 0, n - 1) For (j, 0, i)
{
For (op1, 0, 1) For (op2, 0, 1) For (op3, 0, 1)
(f[i + 1][j][op2][op3][0] += f[i][j][op1][op2][op3]) %= ZZQ;
if (i > 0) For (op1, 0, 1) For (op3, 0, 1)
(f[i + 1][j + 1][1][op3][0] += f[i][j][op1][0][op3]) %= ZZQ;
if (i < n - 1) For (op1, 0, 1) For (op2, 0, 1) For (op3, 0, 1)
(f[i + 1][j + 1][op2][op3][1] += f[i][j][op1][op2][op3]) %= ZZQ;
}
For (i, m, n)
{
int tmp = 0;
For (op1, 0, 1) For (op2, 0, 1)
tmp = (tmp + f[n][i][op1][op2][0]) % ZZQ;
int delta = 1ll * tmp * fac[n - i] % ZZQ * C[i][m] % ZZQ;
if (i - m & 1) ans = (ans - delta + ZZQ) % ZZQ;
else ans = (ans + delta) % ZZQ;
}
cout << ans << endl;
return 0;
}