[BZOJ3512]DZY loves Math IV-數論

VictoryCzt發表於2018-12-23

題目地址

簡易題意

給定n,mn,m,求下面式子在mod 109+7{\rm mod}\ 10^9+7意義下的值:

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

這道題很像,但是這裡n105,m109n\leqslant 10^5,m\leqslant 10^9,資料範圍完全不同了,而且只有單次詢問,所以我們考慮nn很小,所以從後半部分下手。

我們令S(n,m)=i=1mφ(in)S(n,m)=\sum\limits_{i=1}^m\varphi(in),那麼最後的式子就是i=1nS(i,m)\sum\limits_{i=1}^nS(i,m),所以考慮如何快速求取SS函式。

現在只看這個式子:

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

考慮如何把φ(in)\varphi(in)拆開,根據求取φ\varphi的式子:

n=pinpiciφ(n)=npinpi1pi n=\prod_{p_i|n}p_i^{c_i} \\ \varphi(n)=n\prod_{p_i|n}\frac{p_i-1}{p_i}

我們提取每種質因子的一個出來,那麼就變成了:

a=pinpib=pinpici1n=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

因為在例如gcd(i,p)̸=1gcd(i,p)\not = 1時,φ(i×p)=φ(i)×p\varphi(i\times p)=\varphi(i)\times p,而每個質因子之間可以分開獨立考慮,所以上面式子成立。

φ(i×p)=φ(i)×p\varphi(i\times p)=\varphi(i)\times p的證明看這裡:

sample

摘取自友鏈部落格【IN-there-by hdxrie

所以我們把nn變成a,ba,b後,原式變成如下:

S(n,m)=bi=1mφ(ai)=bi=1mφ(agcd(i,a))φ(i)gcd(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}

由於aa中全部是一次質因子的乘積,所以:

φ(ai)=aipkaipk1pk=aipkipk1pkpka,pkipk1pk=ipkipk1pkagcd(i,a)pkagcd(i,a)pk1pk×gcd(i,a)=φ(i)φ(agcd(i,a))gcd(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}

這裡,我們考慮將其拆開,一部分是屬於ii的質因子,而只屬於aa的也就是除去公共質因子的部分,相當於屬於agcd(i,a)\frac{a}{gcd(i,a)}的,但是最後還少了個gcd(i,a)gcd(i,a),再乘上即可。而且這裡由於aa的所有質因子都只出現一次,所以除以了gcd(i,a)gcd(i,a)後肯定與ii互質,不會有重複的pk1pk\frac{p_k-1}{p_k}

然後我們根據φ1=id\varphi*\mathbf 1=id,將後面的gcd(i,a)=dgcd(i,a)φ(d)gcd(i,a)=\sum_{d|gcd(i,a)}\varphi(d),所以:

S(n,m)=bi=1mφ(agcd(i,a))φ(i)dgcd(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}

由於aa中每個質因子只出現一次,所以我們可以將agcd(i,a)\frac{a}{gcd(i,a)}乘到dgcd(i,a)φ(d)\sum_{d|gcd(i,a)}\varphi(d)中的每一項,此時,由於agcd(i,a)\frac{a}{gcd(i,a)}肯定與每個dd互質,根據積性函式的定義:

agcd(i,a)dgcd(i,a)φ(d)=dgcd(i,a)φ(adgcd(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)})

其實就相當於等於:

dgcd(i,a)φ(agcd(i,a)d)=dgcd(i,a)φ(ad) \sum_{d|gcd(i,a)}\varphi(\frac{a}{\frac{gcd(i,a)}{d}})=\sum_{d|gcd(i,a)}\varphi(\frac{a}{d})

因為列舉gcd(i,a)d\frac{gcd(i,a)}{d}dd是一樣的。

所以我們繼續化簡:

S(n,m)=bi=1mφ(i)dgcd(i,a)φ(ad) \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)=bdaφ(ad)dimφ(i)=bdaφ(ad)i=1mdφ(di) \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φ(ad)S(d,md) \begin{aligned} S(n,m)=& b\sum_{d|a}\varphi(\frac{a}{d})S(d,\lfloor\frac{m}{d}\rfloor) \end{aligned}

複雜度和杜教篩類似。

程式碼~~

#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

相關文章