LuoguP3768単純な数学の問題-メビウスの反転+ DuJiaosiの導出の詳細な説明

前提条件のスキル:基本的な数論、メビウスの反転、ディリクレの畳み込み、DuJiaoふるい

トピック

ポータル

回答

(作者はリッチテキストエディタを特に好みますので、以下の導出式はコピーできない写真に表示される場合があります)

質問の意味は非常に単純です。質問してくださいans = \ sum_ {i = 1} ^ n \ sum_ {j = 1} ^ nijgcd(i、j)。モジュラスpは5e8以上の素数なので、逆元を見つけるので安心してください。

以下の式を押し始めます。

gcdがあり、すぐに要約する良い方法がないことがわかったので、gcdを起動しました。

ans = \ sum_ {i = 1} ^ n \ sum_ {j = 1} ^ nijgcd(i、j)= \ sum_ {d = 1} ^ nd ^ 3 \ sum_ {i = 1} ^ {\ left \ lfloor \ frac {n} {d} \ right \ rfloor} \ sum_ {j = 1} ^ {\ left \ lfloor \ frac {n} {d} \ right \ rfloor} ij [gcd(i、j)= 1]

最後の二つ\和_の部分は非常に精通している。我々はメビウス反転を使用することができることを見出した。うmf(x)= \ sum_ {i = 1} ^ {n} \ sum_ {j = 1} ^ {n} ij [gcd(i、j)= x]、   mF(x)= \ sum_ {x | d} mf(x)(後者からそれを区別するためにメビウス反転によって導出関数を表すために使用するMサイズF)、

知覚分析が利用可能です、mF(x)= \ sum_ {x | d} mf(x)= x ^ 2(\ sum_ {i = 1} ^ {\ left \ lfloor \ frac {n} {x} \ right \ rfloor} i)^ 2 = x ^ 2sum(\ left \ lfloor \ frac {n} {x} \ right \ rfloor)^ 2(定義 sum(x)= \ sum_ {i = 1} ^ xi = \ frac {x(x + 1)} {2} )

だから、がありますmf(x)= \ sum_ {x | d} \ mu(\ frac {d} {x})mF(d)= \ sum_ {x | d} \ mu(\ frac {d} {x})d ^ 2sum(\ left \ lfloor \ frac {n} {d} \ right \ rfloor)^ 2

されmf(1)、元に持ち込まans = \ sum_ {d = 1} ^ nd ^ 3 \ sum_ {i = 1} ^ {\ left \ lfloor \ frac {n} {d} \ right \ rfloor} \ mu(i)i ^ 2sum(\ left \ lfloor \ frac {n} {di} \ right \ rfloor)^ 2式:

次に、ループ内にループがあることがわかりました。iを特定の数の因数形式に変更しない限り、\ mu現在の能力と直接合計することはできません(私は不快すぎます)。代わりに、列挙順序を変更します。 dとiを列挙しますが、d * iとdを与えます。

だからT = di、i = \ frac {T} {d}、ありans = \ sum_ {T = 1} ^ nsum(\ left \ lfloor \ frac {n} {T} \ right \ rfloor)^ 2 \ sum_ {d | T} d ^ 3 \ frac {T ^ 2} {d ^ 2} \ mu(\ frac {T} {d})= \ sum_ {T = 1} ^ nsum(\ left \ lfloor \ frac {n} {T} \ right \ rfloor)^ 2T ^ 2 \ sum_ { d | T} d \ mu(\ frac {T} {d})ます;

尾の部分を\ sum_ {d | T} d \ mu(\ frac {T} {d})見ると、私たちが少し慣れていることがわかります。これは、先ほど学習したディリクレの畳み込みの知識によると、\ left \ {\ begin {matrix} \ phi * I = id \\ \ mu * I = \ epsilon \ end {matrix} \ right。 \ Rightarrow \ phi = \ mu * idつまり\ phi(x)= \ sum_ {d | x} d \ mu(\ frac {x} {d})

\ muたくさんの置き換えられたオイラー関数をもたらすことができたので、それは素晴らしいことですans = \ sum_ {T = 1} ^ nsum(\ left \ lfloor \ frac {n} {T} \ right \ rfloor)^ 2T ^ 2 \ sum_ {d | T} d \ mu(\ frac {T} { d})= \ sum_ {T = 1} ^ nsum(\ left \ lfloor \ frac {n} {T} \ right \ rfloor)^ 2T ^ 2 \ phi(T)

設定f(x)= x ^ 2 \ phi(x)してからans = \ sum_ {T = 1} ^ nsum(\ left \ lfloor \ frac {n} {T} \ right \ rfloor)^ 2f(T);

明らかに、値の種類は\ left \ lfloor \ frac {n} {T} \ right \ rfloorせいぜい2 \ sqrt {n}です。f関数の接頭辞の合計をすばやく取得できる場合は、数論を使用してブロックでそれを行うことができます。

したがって、DujiaoSieveを使用します。

設定しS(n)= \ sum_ {i = 1} ^ nf(i)h(i)=(f * g)(i)= \ sum_ {d | i} f(d)g(\ frac {i} {d})

S(n)=(\ sum_ {i = 1} ^ nh(i)-\ sum_ {i = 2} ^ ng(i)S(\ left \ lfloor \ frac {n} {i} \ right \ rfloor) )/ g(1)

ルーチンによれば、G関数はF関数に応じて最適な完全統合関数として設定されるべきである。ここで、gはに設定されg(x)= id ^ 2(x)= x ^ 2、その後、h(i)= \ sum_ {d | i} \ phi(d)d ^ 2 \ frac {i ^ 2} {d ^ 2} = i ^ 2 \ sum_ {d | i} \ phi(d)

によって\ phi * I = id、つまり\ sum_ {d | i} \ phi(d)= id(i)= i、を取得できます h(i)= i ^ 3;

したがって、接頭辞と関数hをすばやく見つける方法が得られます。これは、接頭辞と3次方程式です\ sum_ {i = 1} ^ ni ^ 3 =(\ sum_ {i = 1} ^ ni)^ 2 = sum(n)^ 2

:そして、ドゥのティーチふるい設定しS(n)= \ sum_ {i = 1} ^ ni ^ 3- \ sum_ {i = 1} ^ ni ^ 2S(\ left \ lfloor \ frac {n} {i} \ right \ rfloor)= sum(n )^ 2- \ sum_ {i = 1} ^ ni ^ 2S(\ left \ lfloor \ frac {n} {i} \ right \ rfloor)、(接頭辞と四角式:\ sum_ {i = 1} ^ ni ^ 2 = \ frac {n(n + 1)(2n + 1)} {6}

このとき、数論ブロックを使用すると、f関数の接頭辞の合計をより速く取得できます。

最後に、2つのプロセスが組み合わされている。による杜の時間複雑分析礁渓、複雑さは全てが平坦化されても達成されることはありませんオン)はい、O(n ^ {\ frac {2} {3}})

コード

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<tr1/unordered_map>
#define ll long long
#define MAXN 10000005
using namespace std;
using namespace tr1;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
ll n,p[MAXN],sp[MAXN],MOD=read();
unordered_map<ll,ll>ph;
bool nop[MAXN];
vector<ll>h;
inline ll ksm(ll a,ll b){
	ll res=1;
	for(;b;b>>=1){
		if(b&1)res=res*a%MOD;
		a=a*a%MOD;
	}
	return res;
}
ll NI=ksm(6,MOD-2);//提前求逆元
inline void build(){
	nop[0]=nop[1]=1;
	for(int i=1;i<MAXN-4;i++)p[i]=i;
	for(int a=2;a<MAXN-4;a++){
		if(!nop[a])h.push_back(a),p[a]=a-1;
		for(int i=0,u;i<h.size()&&h[i]*a<MAXN-4;i++){
			u=a*h[i],nop[u]=1;
			if(a%h[i]==0)p[u]=p[a]*h[i],i=MAXN;
			else p[u]=p[a]*p[h[i]];
		}
	}
	sp[1]=p[1];
	for(int i=2;i<MAXN-4;i++)sp[i]=(sp[i-1]+p[i]*i%MOD*i%MOD)%MOD;
}
inline ll sumx(ll n){n%=MOD;//n的范围比模数大就离谱
	return (n*(n+1)>>1)%MOD;   //一次前缀和(sum)
}
inline ll sumy(ll n){n%=MOD;
	return n*(n+1)%MOD*(n*2+1)%MOD*NI%MOD;  //平方前缀和
}
inline ll sumphi(ll n){
	if(n<MAXN-4)return sp[n];
	if(ph.find(n)!=ph.end())return ph[n];
	ll res=sumx(n)*sumx(n)%MOD;
	for(ll i=2,ls;i<=n;i=ls+1)
		ls=n/(n/i),res=(res-(sumy(ls)-sumy(i-1)+MOD)%MOD*sumphi(n/i)%MOD+MOD)%MOD;
	return ph[n]=res;
}
inline ll getans(ll n){
	ll res=0;
	for(ll i=1,ls;i<=n;i=ls+1)
		ls=n/(n/i),res=(res+(sumphi(ls)-sumphi(i-1)+MOD)%MOD*sumx(n/i)%MOD*sumx(n/i)%MOD)%MOD;
	return res;
}
int main()
{
	build();n=read();
	printf("%lld\n",getans(n));
	return 0;
}

 

おすすめ

転載: blog.csdn.net/weixin_43960287/article/details/113483123