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=\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)\)

\(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('\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;
}

完结撒花!!!

posted @ 2020-04-16 00:08  L_G_J  阅读(164)  评论(0编辑  收藏  举报