【洛谷P4240】毒瘤之神的考验

题目

题目链接:https://www.luogu.com.cn/problem/P4240
(Q) 次询问,每次询问给出 (n,m),求

[left(sum^{n}_{i=1}sum^{m}_{j=1}varphi(ij) ight)mod 998244353 ]

(n,mleq 10^5)(Qleq 10^4)

思路

[varphi(i)varphi(j)=iprod_{p|i}frac{p-1}{p} imes jprod_{p|j}frac{p-1}{p} ]

[varphi(i)varphi(j)=ij imes prod_{p|ij}frac{p-1}{p} imesprod_{p|gcd(i,j)}frac{p-1}{p} ]

[varphi(i)varphi(j)gcd(i,j)=ij imes prod_{p|ij}frac{p-1}{p} imes gcd(i,j)prod_{p|gcd(i,j)}frac{p-1}{p} ]

[varphi(ij)=frac{varphi(i)varphi(j)gcd(i,j)}{varphi(gcd(i,j))} ]

所以

[sum^{n}_{i=1}sum^{m}_{j=1}varphi(ij)=sum^{n}_{i=1}sum^{n}_{j=1}frac{varphi(i)varphi(j)gcd(i,j)}{varphi(gcd(i,j))} ]

[=sum^{n}_{d=1}frac{d}{varphi(d)}sum^{n}_{i=1}sum^{n}_{j=1}varphi(i)varphi(j) imes[gcd(i,j)=d] ]

[=sum^{n}_{d=1}frac{d}{varphi(d)}sum^{lfloorfrac{n}{d} floor}_{k=1}mu(k)sum^{lfloorfrac{n}{dk} floor}_{i=1}sum^{lfloorfrac{m}{dk} floor}_{j=1}varphi(idk)varphi(jdk) ]

[=sum^{n}_{k=1}left(sum_{d|k}frac{dmu(frac{k}{d})}{varphi(d)} imes sum^{lfloorfrac{n}{k} floor}_{i=1}varphi(ik) imes sum^{lfloorfrac{m}{k} floor}_{j=1}varphi(jk) ight) ]

(f(i)=sum_{d|i}frac{dmu(frac{i}{d})}{varphi(d)})(g(k,i)=sum^{i}_{j=1}varphi(jk))。前者枚举倍数可以 (O(nlog n)) 预处理出来,后者由于 (k imes ileq n),也可以在 (O(nlog n)) 内预处理。
然后原式

[=sum^{n}_{k=1}f(k) imes g(k,lfloorfrac{n}{k} floor) imes g(k,lfloorfrac{m}{k} floor) ]

整除分块,令 (h(a,b,k)=sum^{n}_{k=1}f(k) imes g(k,a) imes g(k,b))。当然这个不可能全部预处理出来,考虑平衡规划。
设定一个阈值 (B)。预处理出 (max(a,b)leq B) 时所有的 (h),当 (max(a,b)>B) 时,说明 (kleq lfloorfrac{n}{B} floor)。直接暴力求即可。
时间复杂度 (O(nlog n+nB^2+Q(sqrt{n}+frac{n}{B})))。空间复杂度 (O(nB^2))。取 (B=20) 即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=100010,B=20,MOD=998244353;
int Q,n,m,mu[N],phi[N],prm[N];
bool v[N];
ll ans,f[N],h[B+2][B+2][N];
vector<ll> g[N];

void findprm(int n)
{
	mu[1]=phi[1]=1;
	for (int i=2;i<=n;i++)
	{
		if (!v[i]) prm[++m]=i,phi[i]=i-1,mu[i]=-1;
		for (int j=1;j<=m;j++)
		{
			if (i>n/prm[j]) break;
			v[i*prm[j]]=1;
			mu[i*prm[j]]=-mu[i]; phi[i*prm[j]]=phi[i]*(prm[j]-1);
			if (!(i%prm[j]))
			{
				mu[i*prm[j]]=0; phi[i*prm[j]]=phi[i]*prm[j];
				break;
			}
		}
	}
}

ll fpow(ll x,ll k)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%MOD)
		if (k&1) ans=ans*x%MOD;
	return ans;
}

int main()
{
	findprm(N-1);
	for (int i=1;i<N;i++)
	{
		ll inv=fpow(phi[i],MOD-2);		
		for (int j=i;j<N;j+=i)
			f[j]=(f[j]+1LL*i*mu[j/i]*inv)%MOD;
		g[i].push_back(0LL);
		for (int j=1;i*j<N;j++)
			g[i].push_back((g[i][j-1]+phi[i*j])%MOD);
	}
	for (int i=1;i<=B;i++)
		for (int j=1;j<=B;j++)
			for (int k=1;k<N;k++)
				if (i*k<N && j*k<N)
					h[i][j][k]=(h[i][j][k-1]+f[k]*g[k][i]%MOD*g[k][j])%MOD;
	scanf("%d",&Q);
	while (Q--)
	{
		scanf("%d%d",&n,&m);
		if (n>m) swap(n,m);
		ans=0;
		for (int l=1,r;l<=n;l=r+1)
		{
			r=min(n/(n/l),m/(m/l));
			if (m/l<=B) ans=(ans+h[n/l][m/l][r]-h[n/l][m/l][l-1])%MOD;
			else
			{
				for (int i=l;i<=r;i++)
					ans=(ans+f[i]*g[i][n/i]%MOD*g[i][m/i])%MOD;
			}
		}
		cout<<(ans+MOD)%MOD<<"
";
	}
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/15375271.html