$P2398\ GCD\ SUM$


\(Description\)

\(\sum\limits_{i=1}^n\sum\limits_{j=1}^n gcd(i,j)\)

\(Solution\)

這種\(gcd\)計數的題一般思想是枚舉\(gcd\)

對於這道題,有一下幾種做法,循序漸進

暴力:\(O(n^2logn)\)

就是暴力枚舉所有數求\(gcd\),期望得分不清楚,大概\(20pts\)

可以優化\(gcd\)函數,記憶化一下。

畫個矩陣發現可以只求下三角,即只求\(\sum\limits_{i=1}^n\sum\limits_{j=1}^{j<i}gcd(i,j)\),將答案\(*2\)后再單獨用等差數列求和公式處理對角線上的情況\((i==j)\),一頓操作猛如虎,但因為會\(MLE\)仍然無法通過\(70pts\)

離正解只差一步的暴力:\(O(n\sqrt n)\)

枚舉所有數\(i\),設組成數對的另一個數為\(j\),仍是只考慮\(i<j\)的情況。設\(gcd(i,j)=k\),則\(gcd(\frac{i}{k} ,\frac{j}{k})=1\),符合這樣的數\(\frac{j}{k}\)的個數就應該是\(\phi(\frac{i}{k})\),所以\(O(\sqrt n)\)枚舉\(i\)的所有因數為\(gcd\)\(ans+=gcd\cdot \phi(\frac{i}{gcd})\)。總復雜度\(O(n\sqrt n)\)

\(70pts\)肯定是穩過的,至此離正解只差一步。

正解\(O(nln n)\)

上面枚舉因子\(O(\sqrt n)\)顯然是可以優化的,可以先枚舉\(gcd\),再枚舉另一個因子得出\(i=gcd*x\),和上面方法是等效的,調和級數\(O(ln n)\)總復雜度\(O(n ln n)\)

\(Code\)

int n,prime[maxn],pcnt,is_not_prime[maxn];
int phi[maxn];
ll ans;
void Get_Phi(int x)
{
	is_not_prime[1]=1;
	for(re int i=2;i<=x;++i)
	{
		if(!is_not_prime[i]) prime[++pcnt]=i,phi[i]=i-1;
		for(re int j=1;j<=pcnt;++j)
		{
			if(i*prime[j]>x) break;
			is_not_prime[i*prime[j]]=1;
			if(!(i%prime[j]))
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*phi[prime[j]];
		}
	}
}
int vis[maxn];
int main()
{
    n=read();
    Get_Phi(n);
    for(re int i=1;i<=n;++i)//枚舉gcd
		for(re int j=2;j*i<=n;++j)//枚舉數對中較大的數/gcd的結果,從2枚舉是為了不考慮對角線 
		{
			ans+=(ll)phi[j]*i;
		}
	ans*=2;//上三角下三角 
	ans+=(ll)(1+n)*n/2;//統計對角線 
	printf("%lld\n",ans);
	return 0;
}

\(Updata\)

當我在\(luogu\)刷這道題的\(k\)倍經驗時,遇到了一道多組數據且\(T<=200000\)的題,於是成功\(TLE\)
其實只需要對上面算法進行小的改動就行

    ans+=(ll)phi[j]*i;

注意到這一行\(ans\)更新的其實是\(\sum\limits_{x=1}^{x<i*j}gcd(x,i*j)\),相當於對\(i*j\)的答案更新
那么可以這樣寫

    f[i*j]+=(ll)phi[j]*i;

每個答案就是\(\sum\limits_{i=1}^nf[i]\)
所以對於數據個數很多的情況,預處理到最大范圍,求前綴和,\(O(1)\)回答即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM