LGP2522题解

双倍经验题。

柯以看成是P3455的扩展。

首先这个范围内是数我们柯以用类似二维前缀和的思想,看成:
\(ans(a,b,c,d)=ans(1,b,1,d)+ans(1,a-1,1,c-1)-ans(1,b,1,c-1)-ans(1,a-1,1,d)\)
然后计算每一个ans就很好算了。

问题是,怎么算?

每一个ans都是从1开始,柯以看成:

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

然后我们开始推式子:

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

同时除以d:

\[\sum_{i=1}^{\lfloor \frac {n} {d} \rfloor} \sum_{j=1}^{\lfloor \frac {m} {d} \rfloor} [\gcd(i,j)=1] \]

又因为

\[[\gcd(i,j)=1]=e(\gcd(i,j)) \]

而$ 1*\mu=e $
所以

\[[\gcd(i,j)=1]=\sum_{k|gcd(i,j)}\mu(k) \]

然后就变成:

\[\sum_{i=1}^{\lfloor \frac {n} {d} \rfloor} \sum_{j=1}^{\lfloor \frac {m} {d} \rfloor} \sum_{k|\gcd(i,j)} \mu(k) \]

换一个顺序,枚举 \(k\)

\[\sum_{k=1}^{\min(n,m)} \mu(k) * \lfloor \frac {n} {dk} \rfloor * \lfloor \frac{m} {dk} \rfloor \]

这东西柯以用数论分块算。

所以只要先预处理 \(\mu\) 的前缀和,然后数论分块就行了。

Code:

#include<iostream>
const int M=5e4;
int T,a,b,c,d,e,top,mu[M+5],pri[M+5],zhi[M+5];
inline int min(const int&a,const int&b){return a<b?a:b;}
int Ask(int n,int m)//处理答案
{
	int t=min(n/=e,m/=e),L,R,ans=0;
	for(L=1;L<=t;L=R+1)
	{
		R=min(n/(n/L),m/(m/L));
		ans+=(mu[R]-mu[L-1])*(n/L)*(m/L);
	}
	return ans;
}
signed main()
{
	int i,j,x;mu[1]=zhi[1]=1;
	for(i=2;i<=M;++i)//线性筛
	{
		if(!zhi[i])pri[++top]=i,mu[i]=-1;
		for(j=1;j<=top&&(x=i*pri[j])<=M;++j)
		{
			zhi[x]=1;
			if(i%pri[j])mu[x]=-mu[i];
			else break;
		}
		mu[i]+=mu[i-1];//前缀和
	}
	for(std::cin>>T;T;--T)
	{
		std::cin>>a>>b>>c>>d>>e;--a;--c;
		int ans=Ask(b,d)+Ask(a,c)-Ask(b,c)-Ask(a,d);
		std::cout<<ans<<"\n";
	}
}
posted @ 2022-01-10 15:12  Prean  阅读(5)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};