Maximum Element
题目描述
http://codeforces.com/contest/886/problem/E
题解
日常不会计数题。
重度推公式困难症患者。
定义状态f[i]表示i个数离散化成排列,最大的那个放最后面的合法方案数。
那么,枚举n的位置,ans=Σf[i]*A(n-1,n-i)。
考虑转移。对于f[i],考虑第二大的数字i-1。若i-1在i-k前则肯定满足。否则,如果要满足,则以i-1结尾的前缀串离散化后必须要满足条件,这样的方案数位f[j](j位i-1的位置)。
所以得到转移式:f[i]=(i-k-1)(n-2)!+Σf[j] A(n-2,n-j-1)。
这样的dp是O(n^2)的,不能AC。
把排列的式子拆开,把(n-2)!提出来,可以优化成(n-2)!*Σf[j]/(j-1)!。利用前缀和即可把复杂度降到O(n)。
代码
#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
#define N 1000005
using namespace std;
int n,k;ll sum,ans,fac[N],inv[N],f[N];
ll Pow(ll a,int b)
{
ll res=1;
while(b)
{
if(b&1)res=res*a%mod;
a=a*a%mod;b>>=1;
}
return res;
}
ll A(int n,int m){return fac[n]*inv[n-m]%mod;}
int main()
{
scanf("%d%d",&n,&k);fac[0]=1;
for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
inv[n]=Pow(fac[n],mod-2);
for(int i=n;i;i--)inv[i-1]=inv[i]*i%mod;
for(int i=k+2;i<=n;i++)
{
sum=(sum+f[i-1]*inv[i-2]%mod)%mod;
sum=(sum-f[i-k-1]*inv[i-k-2]%mod+mod)%mod;
f[i]=((i-k-1)*fac[i-2]%mod+fac[i-2]*sum%mod)%mod;
ans=(ans+A(n-1,n-i)*f[i]%mod)%mod;
}
printf("%d\n",ans);
return 0;
}