标签:typedef getc har set write 链接 clu 展开 复杂度
从前有一个叫 \(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\) 取模的结果。
真是道有趣的题目呢。
在这道题里,无论我们枚举什么东西,一个大前提显然是:此时函数并未\(return\),由于这是dp专题里的题,我们不妨来\(dp\)它。
设\(f_i\)表示\(1\)~\(i\)的排列中有多少个是运行完并未\(return\)的,那么我们考虑来枚举最大值,显然它出现在\((i-k,i]\)这个区间中,否则会在之前\(return\),那么我们设最大值位置为\(j\),有:
\(f_i=\sum\limits_{j=i-k+1}^i {f_{j-1} \times {{i-1} \choose {i-j}} \times (i-j)!}\),其中\(f_{j-1}\)限制了在之前不能\(return\),\({{i-1} \choose {i-j}}\)意即从现在的\(i-1\)个位置中选出\(i-j\)个扔到\(j\)的前面,\((i-j)!\)即为枚举剩下位置的全排列。
然鹅这是\(O(n^2)\)的,我们来考虑优化。拆式子,
\(f_i=\sum\limits_{j=i-k+1}^i {f_{j-1} \times {{i-1} \choose {i-j}} \times (i-j)!}\)
\(=\sum\limits_{j=i-k+1}^i {f_{j-1} \times (i-1)! \times \frac{1}{(j-1)!}}\)
\(=\sum\limits_{j=i-k+1}^i {f_{j-1} \times (i-1)! \times \frac{1}{(j-1)!}}\)
\(=(i-1)! \sum\limits_{j=i-k}^{i-1}{ \frac{f_j}{j!} }\)
对于\(\sum\limits_{j=i-k}^{i-1}{ \frac{f_j}{j!} }\)这个东西显然可以前缀和优化一下,于是\(dp\)时间就缩短到\(O(n)\)
然后和上面差不多的分析,可以得到\(ans=n!-\sum\limits_{i=1}^n{f_{i-1} \times { n-1 \choose n-i } \times (n-i)!}\),
然后可以展开再约去一项,\(ans=n!-\sum\limits_{i=1}^n{f_{i-1} \times \frac{(n-1)!}{(i-1)!}}\)
于是用\(O(n)\)线性预处理阶乘和阶乘的逆元,总复杂度\(O(n)\)
#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(‘\n‘)
#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;
}
标签:typedef getc har set write 链接 clu 展开 复杂度
原文地址:https://www.cnblogs.com/lgj-lgj/p/12709733.html