CF886E 题解
Posted lgj-lgj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF886E 题解相关的知识,希望对你有一定的参考价值。
题目大意
从前有一个叫 (Petya) 的神仙,嫌自己的序列求(max)太慢了,于是将序列求(max)的代码改成了下面这个样子:
int fast_max(int n,int a[])
{
int ans=0;
int offset=0;
for(int i=0;i<n;++i)
{
if(ans<a[i])
{
ans=a[i];
offset=0;
}
else
{
offset++;
if(offset==k)return ans;
}
}
return ans;
}
新技能get
然鹅这份代码是错的。这位(Petya)神仙对它出错的情况很感兴趣,让你求有多少 (1)~(n) 的排列,这个函数会返回错误的结果,即返回值不是 (n),输出这个数对 (10^9+7) 取模的结果。
(Solution:)
真是道有趣的题目呢。
在这道题里,无论我们枚举什么东西,一个大前提显然是:此时函数并未(return),由于这是dp专题里的题,我们不妨来(dp)它。
设(f_i)表示(1)~(i)的排列中有多少个是运行完并未(return)的,那么我们考虑来枚举最大值,显然它出现在((i-k,i])这个区间中,否则会在之前(return),那么我们设最大值位置为(j),有:
(f_i=sumlimits_{j=i-k+1}^i {f_{j-1} imes {{i-1} choose {i-j}} imes (i-j)!}),其中(f_{j-1})限制了在之前不能(return),({{i-1} choose {i-j}})意即从现在的(i-1)个位置中选出(i-j)个扔到(j)的前面,((i-j)!)即为枚举剩下位置的全排列。
然鹅这是(O(n^2))的,我们来考虑优化。拆式子,
(f_i=sumlimits_{j=i-k+1}^i {f_{j-1} imes {{i-1} choose {i-j}} imes (i-j)!})
(=sumlimits_{j=i-k+1}^i {f_{j-1} imes (i-1)! imes frac{1}{(j-1)!}})
(=sumlimits_{j=i-k+1}^i {f_{j-1} imes (i-1)! imes frac{1}{(j-1)!}})
(=(i-1)! sumlimits_{j=i-k}^{i-1}{ frac{f_j}{j!} })
对于(sumlimits_{j=i-k}^{i-1}{ frac{f_j}{j!} })这个东西显然可以前缀和优化一下,于是(dp)时间就缩短到(O(n))
然后和上面差不多的分析,可以得到(ans=n!-sumlimits_{i=1}^n{f_{i-1} imes { n-1 choose n-i } imes (n-i)!}),
然后可以展开再约去一项,(ans=n!-sumlimits_{i=1}^n{f_{i-1} imes frac{(n-1)!}{(i-1)!}})
于是用(O(n))线性预处理阶乘和阶乘的逆元,总复杂度(O(n))
(Code:)
#include<bits/stdc++.h>
using namespace std;
namespace my_std
{
typedef long long ll;
typedef double db;
#define pf printf
#define pc putchar
#define fr(i,x,y) for(register ll i=(x);i<=(y);++i)
#define pfr(i,x,y) for(register ll i=(x);i>=(y);--i)
#define go(x) for(ll i=head[u];i;i=e[i].nxt)
#define enter pc(‘
‘)
#define space pc(‘ ‘)
#define fir first
#define sec second
#define MP make_pair
const ll inf=0x3f3f3f3f;
const ll inff=1e15;
inline ll read()
{
ll sum=0,f=1;
char ch=0;
while(!isdigit(ch))
{
if(ch==‘-‘) f=-1;
ch=getchar();
}
while(isdigit(ch))
{
sum=sum*10+(ch^48);
ch=getchar();
}
return sum*f;
}
inline void write(ll x)
{
if(x<0)
{
x=-x;
pc(‘-‘);
}
if(x>9) write(x/10);
pc(x%10+‘0‘);
}
inline void writeln(ll x)
{
write(x);
enter;
}
inline void writesp(ll x)
{
write(x);
space;
}
}
using namespace my_std;
const ll N=1e6+50,mod=1e9+7;
ll n,k,mul[N],inv[N],f[N],s[N];
inline ll ksmod(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
{
ans=(ans*a)%mod;
}
a=(a*a)%mod;
b>>=1;
}
return ans;
}
inline void init(ll n)
{
mul[0]=inv[0]=1;
for(ll i=1;i<=n;i++) mul[i]=(mul[i-1]*i)%mod;
inv[n]=ksmod(mul[n],mod-2);
for(ll i=n-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
inline ll C(ll n,ll m)
{
if(m>n||m<0||n<0) return 0;
ll res=inv[m]*inv[n-m]%mod;
res=res*mul[n]%mod;
return res;
}
int main(void)
{
n=read(),k=read();
init(n);
f[0]=s[0]=1;
fr(i,1,n)
{
if(i-k-1>=0) f[i]=mul[i-1]*(s[i-1]-s[i-k-1]+mod)%mod;
else f[i]=mul[i-1]*s[i-1]%mod;
s[i]=(s[i-1]+f[i]*inv[i])%mod;
}
ll ans=0;
fr(i,1,n) ans=(ans+f[i-1]*mul[n-1]%mod*inv[i-1]%mod)%mod;
writeln((mul[n]-ans+mod)%mod);
return 0;
}
完结撒花!!!
以上是关于CF886E 题解的主要内容,如果未能解决你的问题,请参考以下文章