[CQOI2015]选数-题解

题目地址


题意简述

给定 N , K , L , H N,K,L,H N,K,L,H求下面式子在 m o d   1 0 9 + 7 {\rm mod\ 10^9+7} mod 109+7意义下的值。

∑ i 1 = L H ∑ i 2 = L H ⋯ ∑ i N = L H [ g c d ( i 1 , i 2 , ⋯   , i N ) = K ] \sum_{i_1=L}^H\sum_{i_2=L}^H\cdots\sum_{i_N=L}^H[gcd(i_1,i_2,\cdots,i_N)=K] i1=LHi2=LHiN=LH[gcd(i1,i2,,iN)=K]

N , K ≤ 1 0 9 , 1 ≤ L ≤ H ≤ 1 0 9 , H − L ≤ 1 0 5 N,K\leq 10^9 ,1\leq L\leq H\leq 10^9,H-L\leq10^5 N,K109,1LH109,HL105


通过莫比乌斯反演的套路,我们很容易将式子变成如下形式:
∑ i 1 = L ⌊ H K ⌋ ⋯ ∑ i N = L ⌊ H K ⌋ [ g c d ( i 1 , ⋯   , i N ) = 1 ] \sum_{i_1=L}^{\lfloor\frac{H}{K}\rfloor}\cdots\sum_{i_N=L}^{\lfloor\frac{H}{K}\rfloor}[gcd(i_1,\cdots,i_N)=1] i1=LKHiN=LKH[gcd(i1,,iN)=1]

然后将莫比乌斯函数套入,得到:

∑ i 1 = L ⌊ H K ⌋ ⋯ ∑ i N = L ⌊ H K ⌋ ( ∑ w ∣ i 1 , w ∣ i 2 , w ∣ i 3 , ⋯   , w ∣ i N μ ( w ) ) = ∑ w = 1 ⌊ H K ⌋ μ ( w ) ∑ i 1 = L ⌊ H K w ⌋ ⋯ ∑ i N = L ⌊ H K w ⌋ 1 = ∑ w = 1 ⌊ H K ⌋ μ ( w ) ( H K w − L − 1 K w ) N \sum_{i_1=L}^{\lfloor\frac{H}{K}\rfloor}\cdots\sum_{i_N=L}^{\lfloor\frac{H}{K}\rfloor}\left(\sum_{w|i_1,w|i_2,w|i_3,\cdots,w|i_N}\mu(w)\right) \\ =\sum_{w=1}^{\lfloor\frac{H}{K}\rfloor}\mu(w)\sum_{i_1=L}^{\lfloor\frac{H}{Kw}\rfloor}\cdots\sum_{i_N=L}^{\lfloor\frac{H}{Kw}\rfloor}1 \\=\sum_{w=1}^{\lfloor\frac{H}{K}\rfloor}\mu(w)\left(\frac{H}{Kw}-\frac{L-1}{Kw}\right)^N i1=LKHiN=LKHwi1,wi2,wi3,,wiNμ(w)=w=1KHμ(w)i1=LKwHiN=LKwH1=w=1KHμ(w)(KwHKwL1)N

我们用杜教筛筛前面的 μ \mu μ的前缀和,后面分块算即可,复杂度 O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32)

代码~~

#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=2e6+10;
const ll Mod=1e9+7;
ll n,K,L,H,up;
ll prime[M],mu[M],cnt;
bool vis[M];
void init(){
	mu[1]=1;
	for(int i=2;i<=up;i++){
		if(!vis[i]){
			prime[++cnt]=i;
			mu[i]=Mod-1;
		}
		for(int j=1,v;j<=cnt&&i*prime[j]<=up;j++){
			v=i*prime[j];
			vis[v]=1;
			if(!(i%prime[j])){
				break;
			}
			mu[v]=(Mod-mu[i]);
		}
	}
	for(int i=2;i<=up;i++)mu[i]=(mu[i]+mu[i-1])%Mod;
}
ll fpow(ll a,ll b){
	ll res=1;
	for(;b;b>>=1,a=(a*a)%Mod)
		if(b&1)res=(res*a)%Mod;
	return res;
}
map <ll,ll> mp;
ll calc(ll x){
	if(x<=up) return mu[x];
	if(mp.count(x)) return mp[x];
	ll ans=1;
	for(ll i=2,j;i<=x;i=j+1){
		j=(x/(x/i));
		ans=(ans-((j-i+1)%Mod*calc(x/i)%Mod))%Mod;
	}
	if(ans<0)ans+=Mod;
	return mp[x]=ans;
}
ll solve(){
	up=min(H/K+1,M-1ll);
	init();
	ll Max=H/K,ans=0;
	ll t1=H/K,t2=(L-1)/K;
	for(ll i=1,j;i<=Max;i=j+1){
		if(t2/i)j=min(t1/(t1/i),t2/(t2/i));
		else j=t1/(t1/i);
		ans=(ans+(calc(j)-calc(i-1))%Mod*fpow((t1/i-t2/i),n)%Mod)%Mod;
	}
	if(ans<0)ans+=Mod;
	return ans;
}
int main(){
	scanf("%lld%lld%lld%lld",&n,&K,&L,&H);
	printf("%lld\n",solve());
	return 0;
} 

但是我们发现,题面上还有个 H − L ≤ 1 0 5 H-L\leq10^5 HL105的条件,由于在 L ∼ H L\sim H LH中选一些不同的数出来的 g c d ≤ H − L gcd\leq H-L gcdHL,那么就可以枚举 g c d gcd gcd,所以我们可以令 f [ i ] f[i] f[i]为最大公约数为 i i i的答案。

关于为什么在 L ∼ H L\sim H LH中选一些不同的数出来的 g c d gcd gcd不会大于 H − L H-L HL,这里简单证明:
首先我们只用考虑选两个数出来,如果这两个数的 g c d &gt; H − L gcd&gt;H-L gcd>HL,那么令小的那个为 g c d × k 1 gcd\times k_1 gcd×k1,大的为 g c d × k 2 gcd\times k_2 gcd×k2,那么由于它们两个不同,所以 k 2 − k 1 ≥ 1 k_2-k_1\geq 1 k2k11,所以它们两个之间的距离一定大于了 H − L H-L HL,所以不可能同时在 L ∼ H L\sim H LH内。

此时根据前面的转换,最后可以变成答案为 f [ 1 ] f[1] f[1]

下面我们考虑如何求取 f [ i ] f[i] f[i]:

我们从大到小枚举 i i i,我们可以发现,凡是为 i i i的倍数的数都有可能成为答案,所以先加上所有的 i i i的倍数组合后的结果 ( H K i − L − 1 K i ) N − 1 × ( H K i − L − 1 K i − 1 ) \left(\frac{H}{Ki}-\frac{L-1}{Ki}\right)^{N-1}\times\left(\frac{H}{Ki}-\frac{L-1}{Ki}-1\right) (KiHKiL1)N1×(KiHKiL11),但是会多算一些,也就是最大公约数为 2 i , 3 i , ⋯ 2i,3i,\cdots 2i,3i,的方案,所以最后再减去 f [ 2 i ] , f [ 3 i ] , ⋯ f[2i],f[3i],\cdots f[2i],f[3i],即可。

复杂度 O ( ( H − L ) l o g ( H − L ) ) O((H-L)log(H-L)) O((HL)log(HL))

注意 :最后如果 K K K L , H L,H L,H之间的话还要加上全部为 K K K的方案数,也就是1。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=1e5+10;
const ll Mod=1e9+7;
ll n,K,L,H,f[M],len,ls;
ll fpow(ll a,ll b){
	ll res=1;
	for(;b;b>>=1,a=(a*a)%Mod){
		if(b&1)res=(res*a)%Mod;
	}
	return res;
}
int main(){
	scanf("%lld%lld%lld%lld",&n,&K,&L,&H);
	if(L<=K&&K<=H)ls=1;
	ll l=(L-1)/K,r=H/K;
	len=r-l;
	for(int i=len;i>=1;i--){
		ll x=l/i,y=r/i;
		f[i]=fpow(y-x,n)-(y-x);
		if(f[i]<0)(f[i]%=Mod)+=Mod;
		for(ll j=i+i;j<=len;j+=i){
			f[i]=(f[i]-f[j])%Mod;
		}
		if(f[i]<0)f[i]+=Mod;
	}
	printf("%lld\n",f[1]+ls);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VictoryCzt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值