【CF886E】Maximum Element
题意:小P有一个1-n的序列,他想找到整个序列中最大值的出现位置,但是他觉得O(n)扫一遍太慢了,所以它采用了如下方法:
1.逐个遍历每个元素,如果这个元素比当前记录的最大值大,则令最大值等于当前元素,并令cnt=0
2.如果这个元素没有当前元素大,则cnt++。
3.如果cnt=k,则返回当前最大值
现在小P想知道有多少种序列在使用他的方法时会得到错误的答案。为了简化问题,我们假定原序列是一个1-n的排列。即我们要求的是:给定n和k,有多少个1-n的排列,在使用上述算法时,会得到错误的答案。
n,k<=10^6
题解:好难的DP题。
令f[n]表示1-n的排列中,$a_n=n$且能得到错误答案的方案数。显然当n<=K时f[n]都是0。
考虑枚举n-1的出现位置,如果n-1的出现位置在[1,n-k-1]中,那么这样的排列一定是错误的。这样的方案数是(n-2)!*(n-k-1)。
如果n-1在[n-k,n-1]中,那么如果答案是错误的,最大值只能是前n-2个数中的一个。设n-1的位置是j,那么我们将n-1和它前面的数看成一个新的序列,那么这个序列错误的方案数就是f[j]。又由于前面的j个数其实可以在n-2里随便取,所以方案数其实是$f[j]frac {(n-2)!} {(j-1)!}$。
所以DP方程:$f[n]=(n-2)!(n-k-1)+sumlimits_{j=n-k}^{n-1}f[j]frac{(n-2)!}{(j-1)!}$
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; const int maxn=1000010; const ll P=1000000007; int n,m; ll jc[maxn],ine[maxn],jcc[maxn],f[maxn],s[maxn]; int main() { scanf("%d%d",&n,&m); int i; ine[0]=ine[1]=jc[0]=jc[1]=jcc[0]=jcc[1]=1; for(i=2;i<=n;i++) jc[i]=jc[i-1]*i%P,ine[i]=P-(P/i)*ine[P%i]%P,jcc[i]=jcc[i-1]*ine[i]%P; for(i=m+2;i<=n;i++) f[i]=(i-m-1+s[i-1]-s[i-m-1]+P)*jc[i-2]%P,s[i]=(s[i-1]+f[i]*jcc[i-1])%P; printf("%I64d",s[n]*jc[n-1]%P); return 0; }