P1829 [国家集训队]Crash的数字表格 / JZPTAB

原题链接
考察:莫比乌斯反演
思路:
  参考的大佬的题解.这位老师总结的套路总结的很好:GO
主要为:

  1. 优先提取gcd(i,j)
  2. \(gcd(i,j) = d\)化为 \(gcd(i/d,j/d) = 1\)
  3. \(\sum_{d|gcd(i,j)}^{gcd(i,j)} mob[i] = [gcd(i,j)==1]\)
  4. 优先按倍数的方式枚举

解法为两次数论分块,时间复杂度大概在\(O(N)\) 参考题解 GO

Code

#include <iostream>
#include <cstring> 
#include <cstdio>
using namespace std;
typedef long long LL;
const int N = 1e7+10,M = 20101009;
int n,m,prime[N],cnt,mob[N],sum[N];
bool st[N];
LL p;
LL qsm(LL a,LL k,int mod)
{
	LL res=  1;
	while(k)
	{
		if(k&1) res = res*a%mod;
		a = a*a%mod;
		k>>=1;
	}
	return res;
}
void GetPrime(int n)
{
	mob[1] =1;
	for(int i=2;i<=n;i++)
	{
		if(!st[i]) prime[++cnt] = i,mob[i] = -1;
		for(int j=1;prime[j]<=n/i;j++)
		{
			st[i*prime[j]] =1;
			if(i%prime[j]==0) break;
			mob[i*prime[j]] = (-1)*mob[i];
		}
	}
	for(int i=1;i<=n;i++) sum[i] = (sum[i-1]+(LL)i*i%M*mob[i]%M)%M;
}
int calc(int x,int y)
{
	LL res = 0;
	for(int i=1,r;i<=x;i=r+1)
	{
		r = min(x/(x/i),y/(y/i));
		int a = x/i,b=y/i;
		res+=(LL)(sum[r]-sum[i-1])%M*(LL)(1+a)%M*a%M*p%M*(1+b)%M*b%M*p%M;
		res=(res+M)%M;
	}
	return res;
}
int main()
{
	scanf("%d%d",&n,&m);
	if(n>m) swap(n,m);
	GetPrime(N-1);
	LL res =0;
	p = qsm(2,M-2,M);
	for(int i=1,r;i<=n;i=r+1)
	{
		r = min(n/(n/i),m/(m/i));
		res = res+(LL)(i+r)*(r-i+1)%M*p%M*calc(n/i,m/i)%M;
		res=(res+M)%M;
	}
	printf("%lld\n",res);
	return 0;
}
posted @ 2021-06-20 10:14  acmloser  阅读(28)  评论(0编辑  收藏  举报