关于GCD的8题

P2158[SDOI2008]仪仗队

\(Description\)
有一个\(n\times n(1\leq n\leq 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\times\sum_{i=1}^{n-1}\sum_{j=1}^{i-1}[gcd(i,j)=1]+1+2=2\times\sum_{i=2}^{n-1}\phi(i)+3=2\times\sum_{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\n");return 0;}
	Euler();
	for(int i=1;i<n;i++)
		ans+=phi[i];
	printf("%lld\n",ans*2+1);
	return 0;
}

P2568GCD

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


\(Solution\)
\(1\leq x,y\leq n\)\(GCD(x,y)=k\)的对数也就是求\(1\leq x,y\leq n/k\)\(Gcd(x,y)=1\)的对数。有了这个转换,就可以枚举素数。
问题就成了计算\(\sum_p\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{p}\rfloor}[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\n",ans*2-tot);
	return 0;
}

P1447 [NOI2010]能量采集

\(Description\)
\(\sum_{i=1}^n\sum_{j=1}^m2\times(gcd(i,j)-1)+1,1\leq n,m\leq 10^5\)


\(Solution\)

\[ans=2\times\sum_{i=1}^n\sum_{j=1}^mgcd(i,j)-n\times m \]

那么问题就在与求\(\sum_{i=1}^n\sum_{j=1}^mgcd(i,j)\)(默认\(n<m\)),按照前两题的思路,容易想到枚举\(gcd\),也就是求\(\sum_{r=1}^nr\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=r]\)
由于\(i和j\)的取值范围不同,不能像前两题一样用欧拉函数直接计算了,除非用莫比乌斯反演进行转换。但数据范围提示我们这题并不需要做到\(O(n)\)
考虑状态\(f[r]\)表示\(Gcd(i,j)=r\)\((i,j)\)的对数。在\(1\leq x\leq n,1\leq y\leq m\)的范围中,\(x和y\)都是\(r\)的倍数的对数有\(\lfloor\frac{n}{r}\rfloor\times\lfloor\frac{m}{r}\rfloor\),这其中还包含了\(x,y\)都是\(r的2倍、3倍......\),所以\(f[r]=\lfloor\frac{n}{r}\rfloor\times\lfloor\frac{m}{r}\rfloor-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\n",ans*2-1ll*n*m);
	return 0;
}

P2303 [SDOI2012]Longge 的问题

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


\(Solution\)
按照套路枚举

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

\[=\sum_{d|n}d\sum_{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\n",ans);
	return 0;
}

SP7001 VLATTICE - Visible Lattice Points

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


\(Solution\)

posted @ 2021-07-07 23:15  LeTri  阅读(73)  评论(0编辑  收藏  举报