• 关于gcd的四道题


    T1:

    bzoj2705

    题目描述:

    给定一个n求(sumlimits_{i=1}^ngcd(i,n))
    因为n太大,所以O(n)的做法肯定不行,然后就去想根号的方法。

    [sumlimits_{i=1}^{n}gcd(i,n) ]

    [=sumlimits_{k|n}k*sumlimits_{i=1}^n[gcd(i,n)==k] ]

    [=sumlimits_{k|n}k*sumlimits_{i=1}^n[gcd(frac{i}{k},frac{n}{k})==1] ]

    [=sumlimits_{k|n}k*sum_{i=1}^{frac{n}{k}}[gcd(i,frac{n}{k})==1] ]

    [=sum_{k|n}{k*φ(frac{n}{k})} ]

    然后i从1到(sqrt{n})去枚举n的因数,然后将i*φ(n/i)与n/i与φ(i)全部计入答案,就可以做到(sqrt{n}*sqrt{n})的复杂度,因为第二个根号是求欧拉函数的复杂度,所以实际的复杂度没有这么高

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    typedef long long ll;
    ll phi(ll x)
    {
    	ll ans=1;
    	for(ll i=2;i*i<=x;++i)
    	{
    		if(x%i==0)
    		{
    			ans*=(i-1);
    			x/=i;
    		}
    		while(x%i==0)
    		{
    			ans*=i;
    			x/=i;
    		}
    	}
    	if(x!=1)
    	ans*=(x-1);
    	return ans;
    }
    int main()
    {
    	ios::sync_with_stdio(false);
    	ll n;
    	cin>>n;
    	ll ans=0;
    	ll i;
    	for(i=1;i*i<=n;++i)
    		if(n%i==0)
    			ans+=i*phi(n/i)+(n/i)*phi(i);
    	if(i*i==n) ans-=i*phi(i);
    	cout<<ans;
    	return 0;
    }
    

    T2:

    exbzoj2705:

    没有评测,

    题目描述:

    给定一个整数n(1<=n<=100000),你需要求出(sumlimits_{i=1}^nsumlimits_{j=1}^igcd(i,j))
    暴力做法:将上个题中的n循环起来,最后记录每个循环所求的和。明显TLE
    正解:

    [sumlimits_{i=1}^nsumlimits_{j=1}^igcd(i,j) ]

    枚举因数k

    [sumlimits_{k=1}^nk*sumlimits_{i=1}^nsumlimits_{j=1}^i[gcd(i,j)==k] ]

    考虑所有最大公因数为k的情况,设(i=ak,j=bk(a>=b))若要i与j做大公约数为k,则必须满足gcd(a,b)=1,满足此条件的所有情况数为φ(b),然后考虑b的取值范围,因为必须满足b*k<=n,所以(b<=[frac{n}{k}])。所以答案为

    [sumlimits_{k=1}^nk*sumlimits_{i=1}^{frac{n}{k}}φ(i) ]

    所以线性求出欧拉函数,并求出前缀和即可。

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int N=100000+100;
    int phi[N],phi_sum[N];
    void getphi()
    {
    	for(int i=1;i<N;++i)
    		phi[i]=i;
    	phi[1]=1;
    	for(int i=2;i<N;++i)
    		if(phi[i]==i)
    			for(int j=i;j<=N;j+=i)
    				phi[j]=phi[j]/i*(i-1);
    	for(int i=1;i<N;++i)
    		phi_sum[i]=phi_sum[i-1]+phi[i];
    }
    int n;
    int main()
    {
    	getphi();
    	while(1)
    	{
    		scanf("%d",&n);
    		if(!n) break;
    		long long ans=0;
    		for(int i=1;i<=n;++i)
    			ans+=phi_sum[n/i]*i;
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

    T3:

    uva11417

    别问我为什么是luogu

    题目描述:

    给定一个整数n(1<=n<=100000),求(sumlimits_{i=1}^{n-1}sumlimits_{j=i+1}^ngcd(i,j))

    解法:

    [sumlimits_{i=1}^{n-1}sumlimits_{j=i+1}^ngcd(i,j) ]

    枚举因数k

    [sumlimits_{k=1}^nk*sumlimits_{i=1}^{n-1}sumlimits_{j=i+1}^n[gcd(i,j)==k] ]

    考虑所有最大公因数为k的情况,设(i=ak,j=bk(b>a))若要i与j做大公约数为k,则必须满足gcd(a,b)=1,满足此条件的所有情况数为φ(b),然后考虑b的取值范围,因为必须满足b*k<=n,所以(b<=[frac{n}{k}])。所以答案为

    [sumlimits_{k=1}^nk*sumlimits_{i=1}^{frac{n}{k}}φ(i) ]

    所以线性求出欧拉函数,并求出前缀和即可。
    为什么和上面一样
    但是因为i和j都不能为0并且j>i即b>a,所以b不能为1,所以要在最后减去φ(1)的情况,也就相当于把里面的i从2开始枚举。
    所以最终答案为

    [sumlimits_{k=1}^nk*sumlimits_{i=1}^{frac{n}{k}}φ(i)-1 ]

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int N=100000+100;
    int phi[N],phi_sum[N];
    void getphi()
    {
    	for(int i=1;i<N;++i)
    		phi[i]=i;
    	phi[1]=1;
    	for(int i=2;i<N;++i)
    		if(phi[i]==i)
    			for(int j=i;j<=N;j+=i)
    				phi[j]=phi[j]/i*(i-1);
    	for(int i=1;i<N;++i)
    		phi_sum[i]=phi_sum[i-1]+phi[i];
    }
    int n;
    int main()
    {
    	getphi();
    	while(1)
    	{
    		scanf("%d",&n);
    		if(!n) break;
    		long long ans=0;
    		for(int i=1;i<=n;++i)
    			ans+=(phi_sum[n/i]-1)*i;
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

    T4:

    luogu2398

    题目描述:

    给定一个n(1<=n<=100000),求(sumlimits_{i=1}^nsumlimits_{j=1}^ngcd(i,j))

    解法:

    发现这个题数上面两个题的综合,所以,嘿嘿,将上面的两个题答案加起来即可,所以最终答案为

    [sumlimits_{k=1}^n2*k*sumlimits_{i=1}^{frac{n}{k}}φ(i)-1 ]

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    typedef long long ll;
    const int N=100000+100;
    ll ans,phi[N],phi_sum[N],n;
    void getphi()
    {
    	for(int i=1;i<N;++i)
    		phi[i]=i;
    	phi[1]=1;
    	for(int i=2;i<N;++i)
    		if(phi[i]==i)
    			for(int j=i;j<=N;j+=i)
    				phi[j]=phi[j]/i*(i-1);
    	for(int i=1;i<N;++i)
    		phi_sum[i]=phi_sum[i-1]+phi[i];
    }
    int main()
    {
    	cin>>n;
    	getphi();
    	for(int i=1;i<=n;++i)
    		ans+=(phi_sum[n/i]*2-1)*i;
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    【2019.7.26 NOIP模拟赛 T1】数字查找(figure)(数学)
    【2019.7.25 NOIP模拟赛 T1】变换(change)(思维+大分类讨论)
    简析平衡树(四)——FHQ Treap
    【BZOJ3529】[SDOI2014] 数表(莫比乌斯反演)
    【洛谷1829】 [国家集训队] Crash的数字表格(重拾莫比乌斯反演)
    【PE512】Sums of totients of powers(欧拉函数)
    【CFGym102059G】Fascination Street(思维DP)
    【CF438D】The Child and Sequence(线段树)
    【2019.7.16 NOIP模拟赛 T2】折叠(fold)(动态规划)
    【UVA1303】Wall(凸包)
  • 原文地址:https://www.cnblogs.com/wxyww/p/9534392.html
Copyright © 2020-2023  润新知