• 关于GCD的8题


    P2158[SDOI2008]仪仗队

    (Description)
    有一个(n imes n(1leq nleq 40000))的点阵,每个点上有一个人,问站在左下角的人视线中能看到多少人。


    (Solution)
    除去((1,0)和(0,1))两个点,对答案有贡献的其它点都满足(Gcd(x,y)=1)。那么(ans=sum_{i=1}^{n-1}sum_{j=1}^{n-1}[gcd(i,j)=1]+2)

    [sum_{i=1}^{n-1}sum_{j=1}^{n-1}[gcd(i,j)=1] ]

    [=sum_{i=1}^{n-1}sum_{j=1}^{i-1}[gcd(i,j)=1]+sum_{i=1}^{n-1}sum_{j=i}[gcd(i,j)=1]+sum_{i=1}^{n-1}sum_{j=i+1}^{n-1}[gcd(i,j)=1] ]

    容易看出第一项与第三项的值相等,第二项为1。所以(ans=2 imessum_{i=1}^{n-1}sum_{j=1}^{i-1}[gcd(i,j)=1]+1+2=2 imessum_{i=2}^{n-1}+3=2 imessum_{i=1}^{n-1}phi(i)+1)

    #include<complex>
    #include<cstdio>
    using namespace std;
    const int N=4e4+7;
    int n,tot,ans;
    int prime[N],phi[N];
    bool check[N];
    void Euler()
    {
    	check[1]=phi[1]=1;
    	for(int i=2;i<=n;i++)
    	{
    		if(!check[i])prime[++tot]=i,phi[i]=i-1;
    		for(int j=1;j<=tot && i*prime[j]<=n;j++)
    		{
    			check[i*prime[j]]=1;
    			if(i%prime[j])phi[i*prime[j]]=phi[i]*phi[prime[j]];
    			else
    			{
    				phi[i*prime[j]]=phi[i]*prime[j];
    				break;
    			}
    		}
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	if(n==1){printf("0
    ");return 0;}
    	Euler();
    	for(int i=1;i<n;i++)
    		ans+=phi[i];
    	printf("%lld
    ",ans*2+1);
    	return 0;
    }
    

    P2568GCD

    (Description)
    (1leq x,yleq n)(GCD(x,y))为素数的((x,y),1leq nleq 10^7)的对数。


    (Solution)
    (1leq x,yleq n)(GCD(x,y)=k)的对数也就是求(1leq x,yleq n/k)(Gcd(x,y)=1)的对数。有了这个转换,就可以枚举素数。
    问题就成了计算(sum_psum_{i=1}^{lfloorfrac{n}{p} floor}sum_{j=1}^{lfloorfrac{n}{p} floor}[gcd(i,j)=1])。利用上题的思路求解。

    #include<complex>
    #include<cstdio>
    using namespace std;
    const int N=1e7+7;
    int n,tot;
    int prime[N];
    long long phi[N];
    bool check[N];
    void Init()
    {
    	check[1]=phi[1]=1;
    	for(int i=2;i<=n;i++)
    	{
    		if(!check[i])prime[++tot]=i,phi[i]=i-1;
    		for(int j=1;j<=tot && i*prime[j]<=n;j++)
    		{
    			check[i*prime[j]]=1;
    			if(i%prime[j])phi[i*prime[j]]=phi[i]*phi[prime[j]];
    			else
    			{
    				phi[i*prime[j]]=phi[i]*prime[j];
    				break;
    			}
    		}
    	}
    	for(int i=1;i<=n;i++)
    		phi[i]+=phi[i-1];
    }
    int main()
    {
    	scanf("%d",&n);
    	Init();
    	long long ans=0;
    	for(int i=1;i<=tot;i++)
    		ans+=phi[n/prime[i]];
    	printf("%lld
    ",ans*2-tot);
    	return 0;
    }
    

    P1447 [NOI2010]能量采集

    (Description)
    (sum_{i=1}^nsum_{j=1}^m2 imes(gcd(i,j)-1)+1,1leq n,mleq 10^5)


    (Solution)

    [ans=2 imessum_{i=1}^nsum_{j=1}^mgcd(i,j)-n imes m ]

    那么问题就在与求(sum_{i=1}^nsum_{j=1}^mgcd(i,j))(默认(n<m)),按照前两题的思路,容易想到枚举(gcd),也就是求(sum_{r=1}^nrsum_{i=1}^nsum_{j=1}^m[gcd(i,j)=r])
    由于(i和j)的取值范围不同,不能像前两题一样用欧拉函数直接计算了,除非用莫比乌斯反演进行转换。但数据范围提示我们这题并不需要做到(O(n))
    考虑状态(f[r])表示(Gcd(i,j)=r)((i,j))的对数。在(1leq xleq n,1leq yleq m)的范围中,(x和y)都是(r)的倍数的对数有(lfloorfrac{n}{r} floor imeslfloorfrac{m}{r} floor),这其中还包含了(x,y)都是(r的2倍、3倍......),所以(f[r]=lfloorfrac{n}{r} floor imeslfloorfrac{m}{r} floor-f[2r]-f[3r]-...)
    所以倒序枚举(r)求出(f[r])最后就可求出结果。总复杂度为(O(n/1)+O(n/2)+O(n/3)+...=O(nlogn))。虽然比莫比乌斯反演的做法多了一个(log),但是实际代码跑起来还是略快于莫比乌斯反演。

    #include<complex>
    #include<cstdio>
    using namespace std;
    const int N=1e5+7;
    int n,m;
    long long f[N];
    int main()
    {
    	scanf("%d%d",&n,&m);
    	if(n>m)swap(n,m);
    	for(int i=n;i;i--)
    	{
    		f[i]=1ll*n/i*(m/i);
    		for(int j=i+i;j<=n;j+=i)
    			f[i]-=f[j];
    	}
    	long long ans=0;
    	for(int i=1;i<=n;i++)
    		ans+=1ll*i*f[i];
    	printf("%lld
    ",ans*2-1ll*n*m);
    	return 0;
    }
    

    P2303 [SDOI2012]Longge 的问题

    (Description)
    (sum_{i=1}^ngcd(i,n),1leq nleq 2^{32})


    (Solution)
    按照套路枚举

    [sum_{d|n}dsum_{i=1}^n[gcd(i,n)=d] ]

    [=sum_{d|n}dsum_{i=1}^{n/d}[gcd(i,n/d)=1] ]

    可以将(n)质因数分解之后(dfs)枚举(n)的约数。也可以(O(sqrt n))枚举约数并(O(sqrt d))求出(phi(d)),但这种做法求(phi)相互独立,没有用到约数这一性质。由于第一种方法用到底贵,所以两种方法没有明显差别。

    #include<complex>
    #include<cstdio>
    using namespace std;
    const int N=37;
    long long n,tot,ans;
    long long prime[N],num[N];
    void dfs(int x,long long now,long long phi)
    {
    	if(x==tot)
    	{
    		ans+=n/now*phi;
    		return;
    	}
    	dfs(x+1,now,phi);
    	long long t=prime[x+1]-1;
    	for(int i=1;i<=num[x+1];i++,t*=prime[x+1])
    		dfs(x+1,now*=prime[x+1],phi*t);
    }
    int main()
    {
    	scanf("%lld",&n);
    	long long tmp=n;
    	for(int i=2;1ll*i*i<=tmp;i++)
    		if(tmp%i==0)
    		{
    			prime[++tot]=i;
    			for(;tmp%i==0;tmp/=i)
    				num[tot]++;
    		}
    	if(tmp>1)prime[++tot]=tmp,num[tot]=1;
    	dfs(0,1,1);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    SP7001 VLATTICE - Visible Lattice Points

    (Description)
    (Tleq 50)组询问,每次给定一个(n imes n imes n,1leq nleq 10^6)的点阵,问从((0,0,0))能看到多少个不被遮挡的点(就是比仪仗队那题多了一维)。


    (Solution)

  • 相关阅读:
    【MyBatis】MyBatis实现CRUD操作
    【MyBatis】 MyBatis入门
    【MySQL】MySQL数据库再安装
    我的大数据学习历程
    汇编语言——指令系统
    大数据概论
    Python云端系统开发入门——框架基础
    Perl碎碎念
    Oracle函数之LISTAGG
    如何利用Oracle外部表导入文本文件的数据
  • 原文地址:https://www.cnblogs.com/LeTri/p/14984132.html
Copyright © 2020-2023  润新知