[JXOI2018]游戏

[JXOI2018]游戏

​ 链接:https://www.luogu.com.cn/problem/P4562

​ 对于 \(l,r\) 中的数,我们得处理出其中“重要数”的个数,何为“重要数”?即在 \([l,r]\) 这个区间中只有它本身是它的因子的数。这种数在我们检查办公室的过程中是一定要去的。假设我们一共有 \(k\) 个“重要数”。那么最少也要 \(k\) 次,而且走完这 \(k\) 个,全部办公室也就好了。所以 \(t(p)\) 的值,就等于 \(p\) 这个排列中最后一个“重要数” 的位置。我们可以枚举最后一个重要数的位置。那么对于每个位置 \(i\) 对答案的贡献为:

\[C_{i-1}^{k-1}\times i\;\times k!\;\times(r-l+1-k)! \]

\(C_{i-1}^{k-1}\) 表示在这个位置前要选 \(k-1\) 个位置给剩下 \(k-1\) 个数,\(i\) 表示的则是我们构造出来最后一位重要数的位置为 \(i\) 所有排列 \(p\)\(t(p)\) 值。然后对于 \(k\) 个重要数和 \(r-l+1-k\) 个非重要数,我们的位置是固定的,但是顺序不固定,所以分别乘上一个 \(k!\)\((r-l+1-k)!\)

​ 那么接下来我们需要解决求重要数个数的问题。显而易见的,我们可以采取一种类似于埃氏筛的方法来求,代码如下:

	for(int i=l;i<=r;++i)
	{
		if(!p[i])
		{
			++k;
			for(int j=2*i;j<=r;j+=i)
				p[j]=1;			
		}
	}

​ 但是这段代码的复杂度是 \(O(n\times\log\log(n))\),处理数据规模为 \(10^7\) 的数据不够快。(值得一提的是,由于本题数据过水,使用类埃氏筛的方法依然是能够通过本题的)我们可以使用时间复杂度为 \(O(n)\) 的欧拉筛处理出每个数的最小的质因子,然后就可以求出每个数最大的因子,代码如下:

	for(ll i=2;i<=r;++i)
	{
		if(!p[i])
		{
			prime[++prime[0]]=i;
			m[i]=i;
		}
		for(int j=1;j<=prime[0]&&i*prime[j]<=r;++j)
		{
			p[i*prime[j]]=1;
			m[i*prime[j]]=prime[j];
			if(i%prime[j]==0) break;
		}
	}
	m[1]=1;
	for(ll i=l;i<=r;++i)
		if(i/m[i]<l) ++k; 

​ 其中 m 数组表示每个数的最小质因子,至于欧拉筛笔者这里不再赘述,可以去看看这篇博客,讲的非常好:欧拉筛法(线性筛)的学习理解

​ 我们再用快速幂求出 fac[r] 的逆元,然后根据递推式 \(inv_i=inv_{i+1}*(i+1)\)。其中 fac[i] 表示的是 \(i!\)inv[i] 表示的是 fac[i] 的逆元。再根据上面我们推出的对答案的贡献式就好了。

​ 总的时间复杂度为 \(O(n)\)

代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 1e7+5;
const ll MOD = 1e9+7;
bool p[MAXN];
ll inv[MAXN],fac[MAXN],k,l,r,prime[MAXN],m[MAXN];
ll qpw(ll x,ll b)
{
	ll now=1,tmp=x,ans=1;
	while(now<=b)
	{
		if(now&b) ans=ans*tmp%MOD;
		tmp=tmp*tmp%MOD;
		now<<=1;
	}
	return ans;
}
ll c(ll n,ll m)
{
	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int main()
{
	scanf("%d %d",&l,&r);
	for(ll i=2;i<=r;++i)
	{
		if(!p[i])
		{
			prime[++prime[0]]=i;
			m[i]=i;
		}
		for(int j=1;j<=prime[0]&&i*prime[j]<=r;++j)
		{
			p[i*prime[j]]=1;
			m[i*prime[j]]=prime[j];
			if(i%prime[j]==0) break;
		}
	}
	m[1]=1;
	for(ll i=l;i<=r;++i)
		if(i/m[i]<l) ++k; 
	fac[0]=inv[0]=1;
	for(ll i=1;i<=r;++i)
		fac[i]=fac[i-1]*i%MOD;
	inv[r]=qpw(fac[r],MOD-2);
	for(ll i=r-1;i>=1;--i)
		inv[i]=inv[i+1]*(i+1ll)%MOD;
	if(l==1)
	{
		printf("%lld",fac[r-1]*r%MOD*(r+1)%MOD*qpw(2,MOD-2)%MOD);
		return 0;
	}
	ll ans=0;
	for(ll i=k;i<=r-l+1;++i)
	{
		ans+=i*c(i-1,k-1)%MOD*fac[k]%MOD*fac[r-l+1-k]%MOD;
		ans%=MOD;
	}
	printf("%lld",ans);
	return 0;
}

​ 有意思的是,在洛谷的评测机上,类埃氏筛的方法比欧拉筛还要快,只能说 \(\log\log(n)\) 是真的小。。。

posted @ 2021-07-13 19:36  夜空之星  阅读(39)  评论(1编辑  收藏  举报