[BZOJ3512]DZY loves Math IV-数论

题目地址

简易题意

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

∑ i = 1 n ∑ j = 1 m φ ( i j ) \sum_{i=1}^n\sum_{j=1}^m\varphi(ij) i=1nj=1mφ(ij)

这道题很像,但是这里 n ⩽ 1 0 5 , m ⩽ 1 0 9 n\leqslant 10^5,m\leqslant 10^9 n105,m109,数据范围完全不同了,而且只有单次询问,所以我们考虑 n n n很小,所以从后半部分下手。

我们令 S ( n , m ) = ∑ i = 1 m φ ( i n ) S(n,m)=\sum\limits_{i=1}^m\varphi(in) S(n,m)=i=1mφ(in),那么最后的式子就是 ∑ i = 1 n S ( i , m ) \sum\limits_{i=1}^nS(i,m) i=1nS(i,m),所以考虑如何快速求取 S S S函数。

现在只看这个式子:

∑ i = 1 m φ ( i n ) \sum_{i=1}^m\varphi(in) i=1mφ(in)

考虑如何把 φ ( i n ) \varphi(in) φ(in)拆开,根据求取 φ \varphi φ的式子:

n = ∏ p i ∣ n p i c i φ ( n ) = n ∏ p i ∣ n p i − 1 p i n=\prod_{p_i|n}p_i^{c_i} \\ \varphi(n)=n\prod_{p_i|n}\frac{p_i-1}{p_i} n=pinpiciφ(n)=npinpipi1

我们提取每种质因子的一个出来,那么就变成了:

a = ∏ p i ∣ n p i b = ∏ p i ∣ n p i c i − 1 n = a × b φ ( n ) = φ ( a ) × b a=\prod_{p_i|n}p_i \\ b=\prod_{p_i|n}p_i^{c_i-1} \\ n=a\times b \\ \varphi(n)=\varphi(a)\times b a=pinpib=pinpici1n=a×bφ(n)=φ(a)×b

因为在例如 g c d ( i , p ) ̸ = 1 gcd(i,p)\not = 1 gcd(i,p)̸=1时, φ ( i × p ) = φ ( i ) × p \varphi(i\times p)=\varphi(i)\times p φ(i×p)=φ(i)×p,而每个质因子之间可以分开独立考虑,所以上面式子成立。

φ ( i × p ) = φ ( i ) × p \varphi(i\times p)=\varphi(i)\times p φ(i×p)=φ(i)×p的证明看这里:

sample

摘取自友链博客【IN-there-by hdxrie

所以我们把 n n n变成 a , b a,b a,b后,原式变成如下:

S ( n , m ) = b ∑ i = 1 m φ ( a i ) = b ∑ i = 1 m φ ( a g c d ( i , a ) ) φ ( i ) g c d ( i , a ) \begin{aligned} S(n,m)=&b\sum_{i=1}^m\varphi(ai) \\ =& b\sum_{i=1}^m\varphi(\frac{a}{gcd(i,a)})\varphi(i)gcd(i,a) \end{aligned} S(n,m)==bi=1mφ(ai)bi=1mφ(gcd(i,a)a)φ(i)gcd(i,a)

由于 a a a中全部是一次质因子的乘积,所以:

φ ( a i ) = a i ∏ p k ∣ a i p k − 1 p k = a i ∏ p k ∣ i p k − 1 p k ∏ p k ∣ a , p k ∤ i p k − 1 p k = i ∏ p k ∣ i p k − 1 p k a g c d ( i , a ) ∏ p k ∣ a g c d ( i , a ) p k − 1 p k × g c d ( i , a ) = φ ( i ) φ ( a g c d ( i , a ) ) g c d ( i , a ) \begin{aligned} \varphi(ai)=&ai\prod_{p_k|ai}\frac{p_k-1}{p_k} \\ =&ai\prod_{p_k|i}\frac{p_k-1}{p_k}\prod_{p_k|a,p_k\nmid i}\frac{p_k-1}{p_k} \\ =&i\prod_{p_k|i}\frac{p_k-1}{p_k}\frac{a}{gcd(i,a)}\prod_{p_k|\frac{a}{gcd(i,a)}}\frac{p_k-1}{p_k}\times gcd(i,a) \\ =&\varphi(i)\varphi(\frac{a}{gcd(i,a)})gcd(i,a) \end{aligned} φ(ai)====aipkaipkpk1aipkipkpk1pka,pkipkpk1ipkipkpk1gcd(i,a)apkgcd(i,a)apkpk1×gcd(i,a)φ(i)φ(gcd(i,a)a)gcd(i,a)

这里,我们考虑将其拆开,一部分是属于 i i i的质因子,而只属于 a a a的也就是除去公共质因子的部分,相当于属于 a g c d ( i , a ) \frac{a}{gcd(i,a)} gcd(i,a)a的,但是最后还少了个 g c d ( i , a ) gcd(i,a) gcd(i,a),再乘上即可。而且这里由于 a a a的所有质因子都只出现一次,所以除以了 g c d ( i , a ) gcd(i,a) gcd(i,a)后肯定与 i i i互质,不会有重复的 p k − 1 p k \frac{p_k-1}{p_k} pkpk1

然后我们根据 φ ∗ 1 = i d \varphi*\mathbf 1=id φ1=id,将后面的 g c d ( i , a ) = ∑ d ∣ g c d ( i , a ) φ ( d ) gcd(i,a)=\sum_{d|gcd(i,a)}\varphi(d) gcd(i,a)=dgcd(i,a)φ(d),所以:

S ( n , m ) = b ∑ i = 1 m φ ( a g c d ( i , a ) ) φ ( i ) ∑ d ∣ g c d ( i , a ) φ ( d ) \begin{aligned} S(n,m)=& b\sum_{i=1}^m\varphi(\frac{a}{gcd(i,a)})\varphi(i)\sum_{d|gcd(i,a)}\varphi(d) \end{aligned} S(n,m)=bi=1mφ(gcd(i,a)a)φ(i)dgcd(i,a)φ(d)

由于 a a a中每个质因子只出现一次,所以我们可以将 a g c d ( i , a ) \frac{a}{gcd(i,a)} gcd(i,a)a乘到 ∑ d ∣ g c d ( i , a ) φ ( d ) \sum_{d|gcd(i,a)}\varphi(d) dgcd(i,a)φ(d)中的每一项,此时,由于 a g c d ( i , a ) \frac{a}{gcd(i,a)} gcd(i,a)a肯定与每个 d d d互质,根据积性函数的定义:

a g c d ( i , a ) ∑ d ∣ g c d ( i , a ) φ ( d ) = ∑ d ∣ g c d ( i , a ) φ ( a d g c d ( i , a ) ) \frac{a}{gcd(i,a)}\sum_{d|gcd(i,a)}\varphi(d)=\sum_{d|gcd(i,a)}\varphi(\frac{ad}{gcd(i,a)}) gcd(i,a)adgcd(i,a)φ(d)=dgcd(i,a)φ(gcd(i,a)ad)

其实就相当于等于:

∑ d ∣ g c d ( i , a ) φ ( a g c d ( i , a ) d ) = ∑ d ∣ g c d ( i , a ) φ ( a d ) \sum_{d|gcd(i,a)}\varphi(\frac{a}{\frac{gcd(i,a)}{d}})=\sum_{d|gcd(i,a)}\varphi(\frac{a}{d}) dgcd(i,a)φ(dgcd(i,a)a)=dgcd(i,a)φ(da)

因为枚举 g c d ( i , a ) d \frac{gcd(i,a)}{d} dgcd(i,a) d d d是一样的。

所以我们继续化简:

S ( n , m ) = b ∑ i = 1 m φ ( i ) ∑ d ∣ g c d ( i , a ) φ ( a d ) \begin{aligned} S(n,m)=& b\sum_{i=1}^m\varphi(i)\sum_{d|gcd(i,a)}\varphi(\frac{a}{d}) \end{aligned} S(n,m)=bi=1mφ(i)dgcd(i,a)φ(da)

我们交换枚举顺序即可得到:

S ( n , m ) = b ∑ d ∣ a φ ( a d ) ∑ d ∣ i m φ ( i ) = b ∑ d ∣ a φ ( a d ) ∑ i = 1 ⌊ m d ⌋ φ ( d i ) \begin{aligned} S(n,m)=& b\sum_{d|a}\varphi(\frac{a}{d})\sum_{d|i}^{m}\varphi(i) \\ =& b\sum_{d|a}\varphi(\frac{a}{d})\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}\varphi(di) \end{aligned} S(n,m)==bdaφ(da)dimφ(i)bdaφ(da)i=1dmφ(di)

然后可以发现后面部分就是一个子问题了,所以递归求就好啦。

S ( n , m ) = b ∑ d ∣ a φ ( a d ) S ( d , ⌊ m d ⌋ ) \begin{aligned} S(n,m)=& b\sum_{d|a}\varphi(\frac{a}{d})S(d,\lfloor\frac{m}{d}\rfloor) \end{aligned} S(n,m)=bdaφ(da)S(d,dm)

复杂度和杜教筛类似。

代码~~

#include<vector>
#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define lowbit(a) ((a)&(-(a)))
using namespace std;
const int M=1e6+10,N=1e5+10,S=1<<20;
const ll Mod=1e9+7;
ll prime[N],phi[M],F[M],last[M],cnt;
bool vis[M];
int bit[S];
void init(){
	phi[1]=1;
	bit[1]=0;
	for(int i=1;i<=19;i++)bit[1<<i]=i;
	for(ll i=2;i<M;i++){
		if(!vis[i]){
			prime[++cnt]=i;
			phi[i]=i-1;last[i]=1;\\last记录上次的质因子
		}
		for(ll j=1,v;j<=cnt&&i*prime[j]<M;j++){
			v=i*prime[j];
			vis[v]=1;last[v]=i;
			if(!(i%prime[j])){
				phi[v]=phi[i]*prime[j];
				break;
			}
			phi[v]=phi[i]*phi[prime[j]];
		}
	}
	for(int i=1;i<M;i++)F[i]=(F[i-1]+phi[i])%Mod;
}
map <ll,ll> mp_phi;
map <ll,ll> mp_cal[N];
ll calc_phi(ll n){
	if(n<M) return F[n];//杜教筛求phi的前缀和
	if(mp_phi.count(n)) return mp_phi[n];
	ll ans=((n*(n+1ll))>>1)%Mod;
	for(ll i=2,j;i<=n;i=j+1){
		j=(n/(n/i));
		ans=(ans-((j-i+1)%Mod)*calc_phi(n/i)%Mod)%Mod;
	}
	if(ans<0)ans+=Mod;
	return mp_phi[n]=ans;
}
map <ll,ll> mp_p;
ll Getp(ll a){
	if(a<M) return phi[a];
	if(mp_p.count(a)) return mp_p[a];
    ll t=a,tmp=a;
    for(ll j=2;j*j<=a;j++){
        if(t%j==0) {
            a=a/j*(j-1);
            while(t%j==0) t/=j;
        }
    }if(t>1) a=a/t*(t-1);
    return mp_p[tmp]=a;	
}//求单独一个phi
ll calc(ll n,ll m){
	if(n==1) return calc_phi(m);//n=1时就是phi的前缀和
	if(m==1) return Getp(n);//m=1就是一个phi
	if(!m||!n) return 0;//为0就返回0
	if(mp_cal[n].count(m)) return mp_cal[n][m];//记忆化
	vector <ll> div;div.clear();
	ll ans=0,x=1,y,w=1,tc=n;
	while(n>1){
		y=n/last[n];w*=y;n=last[n];
		div.push_back(y);
		while(!(n%y))n/=y,x*=y;
	}
	//w为a,x为b
	n=tc;
	int sze=div.size(),up=1<<sze;
	for(int i=0;i<up;i++){
		ll d=1ll;
		for(int j=i;j>0;j-=lowbit(j))d*=div[bit[lowbit(j)]];
		ans=(ans+(Getp(w/d)*calc(d,m/d))%Mod)%Mod;
	}//2进制枚举约数的质因子组成
	ans=(ans*x)%Mod;//最后乘以b
	return mp_cal[n][m]=ans;
}
int n,m;ll ans;
int main(){
	init();
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)ans=(ans+calc(i,m))%Mod;//累加
	printf("%lld\n",ans);
	return 0;
}

另一种实现方式,较为简单,原理差不多:
挂个链接Orz%%%-IN


End

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

VictoryCzt

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

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

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

打赏作者

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

抵扣说明:

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

余额充值