Description
求n的排列有多少满足:从左到右扫求最大值,当最大值不再变化k次后的最大值不为n。
Sample Input
5 2
Sample Output
22
这题氪了一波金。。。
其实有很多解决方法,在这里我提供两种。
首先定义合法为满足出现一个最大值它是假的。
第一种:
设
f
[
i
]
f[i]
f[i]为填到第
i
i
i个数都是不合法的。
那么可以得到转移:
f
[
i
]
=
∑
j
=
0
m
i
n
(
k
−
1
,
i
−
1
)
f
[
i
−
j
−
1
]
∗
C
(
i
−
1
,
j
)
∗
j
c
[
j
]
f[i]=\sum_{j=0}^{min(k-1,i-1)}f[i-j-1]*C(i-1,j)*jc[j]
f[i]=∑j=0min(k−1,i−1)f[i−j−1]∗C(i−1,j)∗jc[j]
你可以化一下,变成:
f
[
i
]
=
∑
j
=
0
m
i
n
(
k
−
1
,
i
−
1
)
f
[
i
−
j
−
1
]
∗
j
c
[
i
−
j
−
1
]
∗
j
c
[
i
−
1
]
f[i]=\sum_{j=0}^{min(k-1,i-1)}f[i-j-1]*jc[i-j-1]*jc[i-1]
f[i]=∑j=0min(k−1,i−1)f[i−j−1]∗jc[i−j−1]∗jc[i−1]
这个
f
[
i
−
j
−
1
]
∗
j
c
[
i
−
j
−
1
]
f[i-j-1]*jc[i-j-1]
f[i−j−1]∗jc[i−j−1]你可以用一个前缀和来优化它。
最后统计答案时,你枚举n出现的位置。
那么你要保证n前面是合法的,后面就随便填。
那不合法的方案就是:
∑
i
=
1
n
f
[
i
−
1
]
∗
C
(
n
−
1
,
i
−
1
)
∗
j
c
[
n
−
i
]
\sum_{i=1}^nf[i-1]*C(n-1,i-1)*jc[n-i]
∑i=1nf[i−1]∗C(n−1,i−1)∗jc[n−i]
用总数减掉即可。
第二种:
设
f
[
i
]
f[i]
f[i]为填到第
i
i
i个数是合法的。
设
g
[
i
]
g[i]
g[i]为填到第
i
i
i个数是不合法的。
那么你有两种情况的转移,第一种是前面已经构成合法方案:
f
[
i
]
=
f
[
i
−
1
]
∗
C
(
i
,
i
−
1
)
f[i]=f[i-1]*C(i,i-1)
f[i]=f[i−1]∗C(i,i−1) =>
f
[
i
]
=
f
[
i
−
1
]
∗
i
f[i]=f[i-1]*i
f[i]=f[i−1]∗i
第二种是当前这里构成了一个新和方案:
f
[
i
]
=
g
[
i
−
k
−
1
]
∗
C
(
i
−
1
,
k
)
∗
j
c
[
k
]
f[i]=g[i-k-1]*C(i-1,k)*jc[k]
f[i]=g[i−k−1]∗C(i−1,k)∗jc[k]
对于
g
g
g的转移直接用总数减去
f
f
f即可。
我写了第一种。
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
const LL mod = 1e9 + 7;
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
LL inv[1000010], jc[1000010];
LL f[1000010], sum[1000010];
LL pow_mod(LL a, LL k) {
LL ans = 1;
while(k) {
if(k & 1) (ans *= a) %= mod;
(a *= a) %= mod; k /= 2;
} return ans;
}
LL C(LL n, LL m) {return jc[n] * inv[m] % mod * inv[n - m] % mod;}
int main() {
int n = read(), k = read();
jc[0] = 1; for(int i = 1; i <= n; i++) jc[i] = (LL)jc[i - 1] * i % mod;
inv[0] = 1; inv[n] = pow_mod(jc[n], mod - 2);
for(int i = n - 1; i >= 1; i--) inv[i] = (LL)inv[i + 1] * (i + 1) % mod;
sum[1] = 1; f[0] = 1;
for(int i = 1; i <= n; i++) {
f[i] = (sum[i] - sum[_max(0, i - k)]) % mod * jc[i - 1] % mod;
(sum[i + 1] = sum[i] + f[i] * inv[i] % mod) % mod;
} LL ans = 0;
for(int i = 1; i <= n; i++) (ans += f[i - 1] * C(n - 1, i - 1) % mod * jc[n - i] % mod) % mod;
printf("%lld\n", ((jc[n] - ans) % mod + mod) % mod);
return 0;
}