题目链接
链接是洛谷有翻译的。
题意:
有一个长度为
n
n
n的序列,序列的数互不相同。有个人要求这个排列的最大值,他的做法是如果出现了当前值比已经出现过的值都大就更新答案,否则累加一个计数器,如果连续k个数都没有比之前最大值大,那么他就认为当前的最大值就是最后的最大值。问你对于这个序列的所有排列,这种方法有多少种情况下是错的。对1e9+7取模。
n
,
k
<
=
1
e
6
n,k<=1e6
n,k<=1e6
题解:
我们考虑用总的方案数减去所有能用这个方法找到最大值的方案数来求出最终的答案。总方案就是一个
n
!
n!
n!很好求,那么我们就考虑能这样找到最大值的方案数。我们发现其实这个每一个数都不同的序列可以把它看作一个
1
1
1到
n
n
n的排列,因为我们并不关心每个数是什么,只关心一个相对大小关系。我们发现,最大数
n
n
n出现的条件是在它出现之前没有直接退出。那么我们设
d
p
[
i
]
dp[i]
dp[i]表示前
i
i
i个数组成的排列处理到第
i
i
i个位置没有退出的方案数。
由于没有退出,所以
1
−
i
1-i
1−i的最大值一定会出现,那么我们枚举这个最大值出现的位置,不难发现,最大值出现的位置应该在
[
i
−
k
+
1
,
i
]
[i-k+1,i]
[i−k+1,i]才会保证不中途退出。我们枚举这个最大值出现的位置
j
j
j,要求前面的
j
−
1
j-1
j−1个位置也要没有退出,后面从
i
−
1
i-1
i−1个数中选出
i
−
j
i-j
i−j个数,并且这
i
−
j
i-j
i−j个位置的数可以随便排列,于是有
d
p
[
i
]
=
∑
j
=
m
a
x
(
0
,
i
−
k
+
1
)
i
−
1
d
p
[
j
−
1
]
∗
C
i
−
1
i
−
j
∗
(
i
−
j
)
!
dp[i]=\sum_{j=max(0,i-k+1)}^{i-1}dp[j-1]*C_{i-1}^{i-j}*(i-j)!
dp[i]=j=max(0,i−k+1)∑i−1dp[j−1]∗Ci−1i−j∗(i−j)! 把组合数拆成阶乘
d
p
[
i
]
=
∑
j
=
m
a
x
(
0
,
i
−
k
+
1
)
i
−
1
d
p
[
j
−
1
]
∗
(
i
−
1
)
!
(
i
−
j
)
!
(
j
−
1
)
!
∗
(
i
−
j
)
!
=
∑
j
=
m
a
x
(
0
,
i
−
k
+
1
)
i
−
1
d
p
[
j
−
1
]
∗
(
i
−
1
)
!
(
j
−
1
)
!
dp[i]=\sum_{j=max(0,i-k+1)}^{i-1}dp[j-1]*\frac{(i-1)!}{(i-j)!(j-1)!}*(i-j)!=\sum_{j=max(0,i-k+1)}^{i-1}dp[j-1]*\frac{(i-1)!}{(j-1)!}
dp[i]=j=max(0,i−k+1)∑i−1dp[j−1]∗(i−j)!(j−1)!(i−1)!∗(i−j)!=j=max(0,i−k+1)∑i−1dp[j−1]∗(j−1)!(i−1)! 把
(
i
−
1
)
!
(i-1)!
(i−1)!提出
∑
j
=
m
a
x
(
0
,
i
−
k
+
1
)
i
−
1
d
p
[
j
−
1
]
∗
(
i
−
1
)
!
(
j
−
1
)
!
=
(
i
−
1
)
!
∑
j
=
m
a
x
(
0
,
i
−
k
+
1
)
i
−
1
d
p
[
j
−
1
]
(
j
−
1
)
!
\sum_{j=max(0,i-k+1)}^{i-1}dp[j-1]*\frac{(i-1)!}{(j-1)!}=(i-1)!\sum_{j=max(0,i-k+1)}^{i-1}\frac{dp[j-1]}{(j-1)!}
j=max(0,i−k+1)∑i−1dp[j−1]∗(j−1)!(i−1)!=(i−1)!j=max(0,i−k+1)∑i−1(j−1)!dp[j−1] 我们发现这个式子我们可以维护一个
d
p
[
i
−
1
]
(
i
−
1
)
!
\frac{dp[i-1]}{(i-1)!}
(i−1)!dp[i−1]的前缀和,这样就可以
O
(
1
)
O(1)
O(1)转移了。
这样我们就用 O ( n ) O(n) O(n)的时间算出了有多少个长度为 i i i排列没有提前结束。那么在算最终答案的时候,我们用一个类似的dp方程式来算。我们是用所有方案减去能找到最大值的方案。我们设最大值出现在第 i i i个位置,那么能找到最大值就要求前面的没有提前结束,后面的是从 n − 1 n-1 n−1个数中随便选 n − i n-i n−i个,并且这 n − i n-i n−i个数可以随便排列。那么我们最终的答案就是 a n s = n ! − ∑ i = 1 n d p [ i − 1 ] ∗ C n − 1 n − i ∗ ( n − i ) ! ans=n!-\sum_{i=1}^ndp[i-1]*C_{n-1}^{n-i}*(n-i)! ans=n!−i=1∑ndp[i−1]∗Cn−1n−i∗(n−i)! 这个式子也可以 O ( n ) O(n) O(n)算出。于是总复杂度是 O ( n ) O(n) O(n)。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,k;
long long ni[1000010],dp[1000010],jie[1000010],ans,s[1000010];
const long long mod=1e9+7;
inline long long ksm(long long x,long long y)
{
long long res=1;
while(y)
{
if(y&1)
res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
int main()
{
scanf("%d%d",&n,&k);
if(n<=k+1)
{
printf("0\n");
return 0;
}
jie[0]=1;
for(int i=1;i<=n;++i)
jie[i]=jie[i-1]*i%mod;
ni[n]=ksm(jie[n],mod-2);
for(int i=n-1;i>=0;--i)
ni[i]=ni[i+1]*(i+1)%mod;
dp[0]=1;
s[0]=1;
for(int i=1;i<=n;++i)
{
if(i-k-1>=0)
dp[i]=jie[i-1]*(s[i-1]-s[i-k-1]+mod)%mod;
else
dp[i]=jie[i-1]*s[i-1]%mod;
s[i]=(s[i-1]+dp[i]*ni[i]%mod)%mod;
}
ans=jie[n];
for(int i=1;i<=n;++i)
ans=(ans-dp[i-1]*jie[n-1]%mod*ni[i-1]%mod+mod)%mod;
printf("%lld\n",ans);
return 0;
}