洛谷 P1829 [国家集训队]Crash的数字表格 / JZPTAB 莫比乌斯反演

P1829

我又来推式子了(找虐了),假设n<=m

                                  ans=\sum_{i=1}^{n}\sum_{j=1}^{m}\frac{ij}{gcd(i,j)}

这个gcd作分母看起来有点麻烦,我们去掉它:

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

与其枚举两个数看他们的gcd是否等于d,不如直接枚举d的倍数:

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

把d提前:

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

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

代换:

                          ans=\sum_{d=1}^{n}d\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}}\sum_{x|gcd(i,j)}\mu (x)ij        

再次切换枚举,从枚举因数到枚举倍数:

                          ans=\sum_{d=1}^{n}d\sum_{x=1}^{\frac{n}{d}}\mu (x)\sum_{i=1}^{\frac{n}{dx}}\sum_{j=1}^{\frac{m}{dx}}ijx^2

化简:

                         ans=\sum_{d=1}^{n}d\sum_{x=1}^{\frac{n}{d}}\mu (x)x^2(\sum_{i=1}^{\frac{n}{dx}}i)(\sum_{j=1}^{\frac{m}{dx}}j)

接下来我们打表u(x)x^2和等差数列求和,只用枚举d,然后用整除分块求后面的式子就行了。//后面还可以优化哦

#include
#define ll long long
using namespace std;
const int maxn=1e7+5,mod=20101009;
int vis[maxn],pri[maxn],mu[maxn],cnt;
int g[maxn],sum[maxn];
void get_mu(int n)
{
	mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		pri[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&pri[j]*i<=n;j++)
		{
			vis[i*pri[j]]=1;
			if(i%pri[j]==0)break;
			else mu[i*pri[j]]=-mu[i];
		}
	}
	for(int i=1;i<=n;i++)
	{
		sum[i]=(sum[i-1]+i)%mod;
		g[i]=(g[i-1]+1ll*mu[i]*i*i%mod)%mod;	
	}
}
int main()
{
	int n,m;
	ll ans=0;
	get_mu(1e7);
	scanf("%d%d",&n,&m);
	if(n>m)swap(n,m);
	for(int i=1;i<=n;i++)
	{
		int nn=n/i,mm=m/i;
		for(int l=1,r;l<=nn;l=r+1)
		{
			r=min(nn/(nn/l),mm/(mm/l));
			ans+=1ll*i*(g[r]-g[l-1]+mod)%mod*sum[nn/l]%mod*sum[mm/r]%mod;
		}
	}
	printf("%lld\n",ans%mod);
}

其实我们可以把第一层for循环枚举也来整除分块,这样更快哦。

#include
#define ll long long
using namespace std;
const int maxn=1e7+5,mod=20101009;
int vis[maxn],pri[maxn],mu[maxn],cnt;
int g[maxn],sum[maxn];
void get_mu(int n)
{
	mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		pri[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&pri[j]*i<=n;j++)
		{
			vis[i*pri[j]]=1;
			if(i%pri[j]==0)break;
			else mu[i*pri[j]]=-mu[i];
		}
	}
	for(int i=1;i<=n;i++)
	{
		sum[i]=(sum[i-1]+i)%mod;
		g[i]=(g[i-1]+1ll*mu[i]*i*i%mod)%mod;	
	}
}
ll work(int n,int m)
{
	ll ans=0;
	for(int l=1,r;l<=n;l=r+1)
	{
		r=min(n/(n/l),m/(m/l));
		ans+=1ll*(g[r]-g[l-1]+mod)%mod*sum[n/l]%mod*sum[m/r]%mod;
	}	
	return ans;
}
ll cal(int n)
{
	return 1ll*n*(n+1)/2%mod;
}
int main()
{
	int n,m;
	ll ans=0;
	get_mu(1e7);
	scanf("%d%d",&n,&m);
	if(n>m)swap(n,m);
	for(int l=1,r;l<=n;l=r+1)
	{
		r=min(n/(n/l),m/(m/l));
		ans+=(cal(r)-cal(l-1)+mod)*work(n/l,m/l)%mod;
	}
	printf("%lld\n",ans%mod);
}

 

你可能感兴趣的:(数学----莫比乌斯反演)