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 题解的主要内容,如果未能解决你的问题,请参考以下文章

CF886E Maximum Element

cf 模拟

题解 CF1063B Labyrinth

CF Round #631 题解

CF524B 题解

题解 CF1216D Swords