【刷题】BZOJ 3930 [CQOI2015]选数

Description

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

Input

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

Output

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

Sample Input

2 2 2 4

Sample Output

3

HINT

样例解释
所有可能的选择方案:(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≤109,1≤L≤H≤109,H-L≤10^5

Solution

我用的是莫比乌斯反演加杜教筛
这题本来不需要莫反的,但最近都在练习莫反,那就用莫反做了
\(F(d)\)代表在\([L,R]\)中选N个数,它们的gcd为d及其倍数的方案数
\(f(d)\)代表在\([L,R]\)中选N个数,它们的gcd为d的方案数

\[F(n)=\sum_{n|d}f(d)=(\lfloor \frac{R}{n} \rfloor-\lfloor \frac{L-1}{n} \rfloor)^N \]

上面式子的左边一半根据定义,右边一半的原因如下:
\(\lfloor \frac{R}{n} \rfloor\)其实是1到R中有多少个数整除n,\(\lfloor \frac{L-1}{n} \rfloor\)类似,那么它们相减之后,得到的就是\([L,R]\)中有多少个数可以整除n。根据题目的第一句话,我们知道选数是有序的,并且可以重复选。所以我们在得到了有多少个数整除n后,只要在里面有序地任选N个数,方案数是\((\lfloor \frac{R}{n} \rfloor-\lfloor \frac{L-1}{n} \rfloor)^N\),它们可以保证它们的gcd一定为n或n的倍数
接下来继续推

\[f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)=\sum_{n|d}\mu(\frac{d}{n})(\lfloor \frac{R}{d} \rfloor-\lfloor \frac{L-1}{d} \rfloor)^N \]

改变枚举方式

\[f(n)=\sum_{d=1}^{\lfloor \frac{R}{n} \rfloor}\mu(d)(\lfloor \frac{R}{nd} \rfloor-\lfloor \frac{L-1}{nd} \rfloor)^N \]

后面的东西整除分段加快速幂,前面的东西杜教筛
关于杜教筛,这里只给一个式子,有兴趣可以百度

\[S(n)=1-\sum_{i=2}^nS(\lfloor \frac{n}{i} \rfloor)\ \ \ \ (S(n)=\sum_{i=1}^n\mu(i)) \]

#include<bits/stdc++.h>
#define ll long long
const int Mod=1e9+7,MAXN=1e6+10,inf=0x3f3f3f3f;
int prime[MAXN],cnt,vis[MAXN],s[MAXN],mu[MAXN];
std::map<ll,ll> M;
template<typename T> inline void read(T &x)
{
	T data=0,w=1;
	char ch=0;
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
	x=data*w;
}
template<typename T> inline void write(T x,char c='\0')
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
	if(c!='\0')putchar(c);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void init()
{
	memset(vis,1,sizeof(vis));
	vis[0]=vis[1]=0;
	mu[1]=1;
	for(register int i=2;i<MAXN;++i)
	{
		if(vis[i])
		{
			prime[++cnt]=i;
			mu[i]=-1;
		}
		for(register int j=1;j<=cnt&&i*prime[j]<MAXN;++j)
		{
			vis[i*prime[j]]=0;
			if(i%prime[j])mu[i*prime[j]]=-mu[i];
			else break;
		}
	}
	for(register int i=1;i<MAXN;++i)s[i]=s[i-1]+mu[i];
}
inline ll qexp(ll a,ll b)
{
	ll res=1;
	while(b)
	{
		if(b&1)res=res*a%Mod;
		a=a*a%Mod;
		b>>=1;
	}
	return res;
}
inline ll MuSum(int x)
{
	if(x<MAXN)return s[x];
	if(M[x])return M[x];
	ll res=1;
	for(register int i=2;;)
	{
		if(i>x)break;
		int j=x/(x/i);
		res-=(ll)(j-i+1)*(ll)MuSum(x/i);
		i=j+1;
	}
	return M[x]=res;
}
inline ll solve(int N,int L,int H)
{
	ll res=0;
	for(register int i=1;;)
	{
		if(i>H)break;
		int j=min(H/(H/i),L/i?L/(L/i):inf);
		(res+=qexp((ll)(H/i-L/i),N)*(ll)(MuSum(j)-MuSum(i-1))%Mod)%=Mod;
		i=j+1;
	}
	return (res+Mod)%Mod;
}
int main()
{
	init();
	int N,K,L,H;
	read(N);read(K);read(L);read(H);
	write(solve(N,(L-1)/K,H/K),'\n');
	return 0;
}
posted @ 2018-03-10 16:16  HYJ_cnyali  阅读(205)  评论(0编辑  收藏  举报