[CQOI2015]选数

Posted by Dispwnl on February 23, 2019

题目

题目描述

我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数K,你需要回答他最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可。

输入输出格式

输入格式:

输入一行,包含4个空格分开的正整数,依次为N,K,L和H。

输出格式:

输出一个整数,为所求方案数。

输入输出样例

输入样例#1:

2 2 2 4

输出样例#1:

3

说明

样例解释

所有可能的选择方案:(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)

其中最大公约数等于2的只有3组:(2, 2), (2, 4), (4, 2)

对于100%的数据,1<=N,K<=10^9,1<=L<=H<=10^9,H-L<=10^5

题解

设$f(i)​$表示$\gcd(a_1,a_2,…,a_{n-1},a_n)=i​$的方案数

设$F(x)=\sum_{x\vert d}f(d)$,这样$F(x)$就表示$x\vert \gcd(a_1,a_2,…,a_{n-1},a_n)$的方案数

即$F(x)=(\left\lfloor\frac{H}{x}\right\rfloor-\left\lfloor\frac{L-1}{x}\right\rfloor)^n​$

所以$f(x)=\sum_{x\vert d}\mu(\frac{d}{x})F(x)​$

可以缩小范围到$[\left\lfloor\frac{L}{K}\right\rfloor,\left\lfloor\frac{H}{K}\right\rfloor]​$,这样答案就是$f(1)​$

$f(1)=\sum_{i=1}^{\left\lfloor\frac{H}{K}\right\rfloor}\mu(i)F(i)=\sum_{i=1}^{\left\lfloor\frac{H}{K}\right\rfloor}\mu(i)(\left\lfloor\frac{H}{i}\right\rfloor-\left\lfloor\frac{L-1}{i}\right\rfloor)^n​$

后面那个东西可以整除分块分小于等于$L-1$和大于$L-1$讨论,既然$H$的范围是$10^9$,所以用杜教筛处理$\mu$函数

注意因为要求的不是一个特定的$\mu$的前缀和,所以不能只记录$\frac{n}{i}$的值,要用map

代码

# include<iostream>
# include<cstring>
# include<map>
# include<cstdio>
# include<algorithm>
using namespace std;
const int MAX=1e7+5,N=205,mod=1e9+7;
int n,K,L,R,tot,ans;
int pm[MAX],u[MAX];
bool use[MAX],vis[N];
map<int,int> mu;
void dfs(int x)
{
	if(x<MAX) return;
	if(mu.find(x)!=mu.end()) return;
	int ans=1;
	for(int l=2,r,N;l<=x;l=r+1)
	  N=x/l,r=x/N,dfs(N),ans-=(r-l+1)*(N<MAX?u[N]:mu[N]);
	mu[x]=(ans+mod)%mod;
}
void ss(int n)
{
	u[1]=1;
	for(int i=2;i<=n;++i)
	  {
	  	if(!use[i]) pm[++tot]=i,u[i]=-1;
	  	for(int j=1;j<=tot&&pm[j]*i<=n;++j)
	  	  {
	  	  	use[pm[j]*i]=1;
	  	  	if(i%pm[j]==0) break;
	  	  	u[pm[j]*i]=-u[i];
		  }
		u[i]+=u[i-1];
	  }
}
int Pow(int a,int b)
{
	int ans=1;
	for(;b;b>>=1,a=1ll*a*a%mod)
	  if(b&1) ans=1ll*ans*a%mod;
	return ans;
}
int GET_MU(int x) {return x<MAX?u[x]:mu[x];}
int main()
{
	scanf("%d%d%d%d",&n,&K,&L,&R),ss(min(R/=K,MAX-1)),L=(L-1)/K;
	for(int l=1,r,N,M;l<=R;l=r+1)
	  {
	  	N=R/l,r=R/N,M=0;
	  	if(l<=L) M=L/l,r=min(r,L/M);
		dfs(r),dfs(l-1),ans+=1ll*(GET_MU(r)-GET_MU(l-1)+mod)%mod*Pow(N-M,n)%mod;
	  	if(ans>=mod) ans-=mod;
	  }
	return printf("%d",ans),0;
}