Pbri

P3768做题笔记

P3768 做题笔记

神必题给 \(n\) 开到了 \(10^{10}\) ,一个地方没取模直接爆 \(long\,long\) 了,调了一晚上吐了。

一个做法是常规的莫比乌斯反演!最后的做法和下面的做法差不多。

但莫比乌斯反演的式子看起来就很长,而且好像还需要卷一下 \(\varphi\) 一类的东西,推起来非常麻烦,结果也不够精简,我们可以直接用 \(\varphi\) 来表示那个 \(\gcd\)

推式子: \(\sum_{i=1}^n\sum_{j=1}^nij\gcd(i,j)\)

\(=\sum_{i=1}^n\sum_{j=1}^nij\sum_{d|\gcd(i,j)}\varphi(d)\)

\(=\sum_{d=1}^n\varphi(d)d^2(\sum_{i=1}^{n/d}i)^2\)

然后我们就可以非常开心的数论分块,现在问题转化成为如何快速求 \(\sum_{i=l}^r\varphi(i)i^2\)

首先化成前缀和,然后是个积性函数,我们考虑杜教筛。令 \(g(i)=\text{id}^2(i)\varphi(i)\) ,那么我们需要一个 \(h(i)\) 使得可以方便的求 \(g*h\) 的前缀和以及 \(h\) 的前缀和。我们可以考虑如下的引理:

定义点乘为逐项相乘,即 \((f\cdot g)(n)=f(n)\times g(n)\) ,那么设 \(f(i)\) 为完全积性函数(即不要求互质),\(u(i),v(i)\) 为数论函数,那么有

\[(f\cdot u)*(f\cdot v)=f\cdot (u*v) \]

证明:

\(((f\cdot u)*(f\cdot v))(n)=\sum_{d|n}f(d)\times u(d)\times f(\dfrac{n}{d}) \times v(\dfrac{n}{d})=f(n)\times\sum_{d|n}u(d)v(\dfrac{n}{d})=f\cdot(u*v)\)

那么再看回这个题,我们设 \(f=\text{id}^2,u=\varphi,v=\text{1}\) 就有 \(g=f\cdot v=\text{id}^2,g*h=(f\cdot u)*(f\cdot v)=\text{id}^2\cdot(\varphi*1)=\text{id}^3\)

然后我们就可以愉快的杜教筛啦

注意时时取模

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <map>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 2000010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define ll long long
#define db double
using namespace std;
ll read()
{
	ll w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
ll MOD,n,inv2,inv6;
const int yz=2000000;
map<int,ll>mp;
ll phi[MAXN],phisum[MAXN],prnum,prm[MAXN];
bool vis[MAXN];
void sieve()
{
	phi[1]=1;
	FUP(i,2,yz)
	{
		if(!vis[i]) prm[++prnum]=i,phi[i]=(ll)(i)*i%MOD*(i-1)%MOD;
		FUP(j,1,prnum)
		{
			if(i*prm[j]>yz) break;
			vis[i*prm[j]]=true;
			if(i%prm[j]) phi[i*prm[j]]=phi[i]*phi[prm[j]]%MOD;
			else
			{
				phi[i*prm[j]]=phi[i]*prm[j]%MOD*prm[j]%MOD*prm[j]%MOD;
				break;
			}
		}
	}
	FUP(i,1,yz) phisum[i]=(phisum[i-1]+phi[i]+MOD)%MOD;
}
ll poww(ll a,ll b)
{
	ll ans=1,base=a;
	while(b)
	{
		if(b&1) ans=ans*base%MOD;
		base=base*base%MOD;
		b>>=1;
	}
	return ans;
}
ll tsum(ll x)
{
	x%=MOD;
	return x*(x+1)%MOD*(2*x+1)%MOD*inv6%MOD;
}
ll sum3(ll x)
{
	x%=MOD;
	ll w=x*(x+1)%MOD*inv2%MOD;
	return w*w%MOD;
}
ll calc(ll x)
{
	if(x<=yz) return phisum[x];
	if(mp[x]) return mp[x];
	ll ans=sum3(x);
	for(ll l=2,r;l<=x;l=r+1)
	{
		r=x/(x/l);
		ans=(ans-(tsum(r)-tsum(l-1)+MOD)%MOD*calc(x/l)%MOD+MOD)%MOD; 
	}
	mp[x]=ans;
	return ans;
}
ll ans;
int main(){
	MOD=read(),n=read(),inv2=poww((ll)2,MOD-2),inv6=poww((ll)6,MOD-2);
	//printf("%lld\n",2*inv2%MOD);
	sieve();
	for(ll l=1,r;l<=n;l=r+1)
	{
		r=n/(n/l);
		ll w1=(calc(r)-calc(l-1)+MOD)%MOD;
		ll w2=sum3(n/l);
		//printf("%lld %lld %lld\n",ans,w1,w2);
		ans=(ans+w1*w2%MOD)%MOD;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-08-05 23:01  Pbri  阅读(15)  评论(0编辑  收藏  举报