[Luogu P3768] 简单的数学题

洛谷传送门

题目描述

由于出题人懒得写背景了,题目还是简单一点好。

输入一个整数 n n n和一个整数 p p p,你需要求出 ( ∑ i = 1 n ∑ j = 1 n i j g c d ( i , j ) )   m o d   p (\sum_{i=1}^n\sum_{j=1}^n ijgcd(i,j))~mod~p (i=1nj=1nijgcd(i,j)) mod p,其中 g c d ( a , b ) gcd(a,b) gcd(a,b)表示 a a a b b b的最大公约数。

刚才题面打错了,已修改

输入输出格式

输入格式:

一行两个整数 p p p n n n

输出格式:

一行一个整数 ( ∑ i = 1 n ∑ j = 1 n i j g c d ( i , j ) )   m o d   p (\sum_{i=1}^n\sum_{j=1}^n ijgcd(i,j))~mod~p (i=1nj=1nijgcd(i,j)) mod p

输入输出样例

输入样例#1:
998244353 2000
输出样例#1:
883968974

说明

对于20%的数据, n ≤ 1000 n \leq 1000 n1000

对于30%的数据, n ≤ 5000 n \leq 5000 n5000

对于60%的数据, n ≤ 1 0 6 n \leq 10^6 n106,时限1s。

对于另外20%的数据, n ≤ 1 0 9 n \leq 10^9 n109,时限3s。

对于最后20%的数据, n ≤ 1 0 10 n \leq 10^{10} n1010,时限6s。

对于100%的数据, 5 × 1 0 8 ≤ p ≤ 1.1 × 1 0 9 5 \times 10^8 \leq p \leq 1.1 \times 10^9 5×108p1.1×109 p p p为质数。

解题分析

大力推式子:
∑ i = 1 n ∑ j = 1 n i j g c d ( i , j ) = ∑ d = 1 n d ∑ i = 1 n ∑ j = 1 n i j [ g c d ( i , j ) = d ] = ∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ i j d 2 [ g c d ( i , j ) = 1 ] = ∑ d = 1 n d 3 ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ i j ∑ p ∣ g c d ( i , j ) μ ( p ) = ∑ d = 1 n d 3 ∑ p = 1 ⌊ n d ⌋ μ ( p ) p 2 ∑ i = 1 ⌊ n d p ⌋ ∑ j = 1 ⌊ n d p ⌋ i j \sum_{i=1}^n\sum_{j=1}^nijgcd(i,j) \\ =\sum_{d=1}^nd\sum_{i=1}^n\sum_{j=1}^nij[gcd(i,j)=d] \\ =\sum_{d=1}^nd\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{d}\rfloor}ijd^2[gcd(i,j)=1] \\ =\sum_{d=1}^nd^3\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{d}\rfloor}ij\sum_{p|gcd(i,j)}\mu(p) \\ =\sum_{d=1}^nd^3\sum_{p=1}^{\lfloor\frac{n}{d}\rfloor}\mu(p)p^2\sum_{i=1}^{\lfloor\frac{n}{dp}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{dp}\rfloor}ij i=1nj=1nijgcd(i,j)=d=1ndi=1nj=1nij[gcd(i,j)=d]=d=1ndi=1dnj=1dnijd2[gcd(i,j)=1]=d=1nd3i=1dnj=1dnijpgcd(i,j)μ(p)=d=1nd3p=1dnμ(p)p2i=1dpnj=1dpnij
s ( n ) = ∑ i = 1 n i s(n)=\sum_{i=1}^ni s(n)=i=1ni, 并设 T = d p T=dp T=dp, 那么:
∑ d = 1 n d 3 ∑ p = 1 ⌊ n d ⌋ μ ( p ) p 2 ∑ i = 1 ⌊ n d p ⌋ ∑ j = 1 ⌊ n d p ⌋ i j = ∑ T = 1 n s ( n T ) 2 T 2 ∑ d ∣ T d μ ( T d ) \sum_{d=1}^nd^3\sum_{p=1}^{\lfloor\frac{n}{d}\rfloor}\mu(p)p^2\sum_{i=1}^{\lfloor\frac{n}{dp}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{dp}\rfloor}ij \\ =\sum_{T=1}^{n}s(\frac{n}{T})^2T^2\sum_{d|T}d\mu(\frac{T}{d}) d=1nd3p=1dnμ(p)p2i=1dpnj=1dpnij=T=1ns(Tn)2T2dTdμ(dT)
而我们知道 ϕ ( i ) = μ ( i ) ∗ i d ( i ) \phi(i)=\mu(i)*id(i) ϕ(i)=μ(i)id(i), 那么实际上这玩意就是:
∑ T = 1 n s ( n T ) 2 T 2 ϕ ( T ) \sum_{T=1}^{n}s(\frac{n}{T})^2T^2\phi(T) T=1ns(Tn)2T2ϕ(T)
前面可以下底分块, 关键在于后面的 f ( x ) = x 2 ϕ ( x ) f(x)=x^2\phi(x) f(x)=x2ϕ(x)上面, 这玩意该怎么科学筛出前缀和。

考虑到 ϕ ( i ) ∗ I = i d ( i ) \phi(i)*I=id(i) ϕ(i)I=id(i), 我们可以设 g ( x ) = x 2 g(x)=x^2 g(x)=x2, 那么
h ( x ) = g ( x ) ∗ f ( x ) = ∑ d ∣ x d 2 ∗ ϕ ( x d ) ( x d ) 2 = ∑ d ∣ x x 2 ϕ ( x ) = x 3 h(x)=g(x)*f(x) \\=\sum_{d|x}d^2*\phi(\frac{x}{d})(\frac{x}{d})^2 \\=\sum_{d|x}x^2\phi(x) \\ =x^3 h(x)=g(x)f(x)=dxd2ϕ(dx)(dx)2=dxx2ϕ(x)=x3
那么根据这个套路的式子:
g ( 1 ) S ( n ) = ∑ i = 1 n h ( i ) − ∑ d = 2 n g ( d ) S ( ⌊ n d ⌋ ) g(1)S(n)=\sum_{i=1}^nh(i)-\sum_{d=2}^{n}g(d)S(\lfloor\frac{n}{d}\rfloor) g(1)S(n)=i=1nh(i)d=2ng(d)S(dn)
∑ i = 1 n h ( i ) = ( x + 1 ) 2 x 2 4 , ∑ i = 1 n g ( i ) = n ∗ ( n + 1 ) ∗ ( 2 n + 1 ) 6 \sum_{i=1}^{n}h(i)=\frac{(x+1)^2x^2}{4},\sum_{i=1}^{n}g(i)=\frac{n*(n+1)*(2n+1)}{6} i=1nh(i)=4(x+1)2x2,i=1ng(i)=6n(n+1)(2n+1), 就可以科学筛出来了。

代码如下:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <tr1/unordered_map>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 5005000
#define ll long long
int pcnt;
std::tr1::unordered_map <ll, ll> mp;
int phi[MX], pri[MX];
ll sum[MX], val[MX], MOD, inv2, inv4, inv6;
bool npr[MX];
void get()
{
	phi[1] = 1, val[1] = 1;
	R int i, j, tar;
	for (i = 2; i <= 5e6; ++i)
	{
		if (!npr[i]) pri[++pcnt] = i, phi[i] = i - 1;
		for (j = 1; j <= pcnt; ++j)
		{
			tar = i * pri[j];
			if (tar > 5e6) break;
			npr[tar] = true;
			if (!(i % pri[j])) {phi[tar] = phi[i] * pri[j]; break;}
			phi[tar] = phi[i] * phi[pri[j]];
		}
	}
	for (i = 1; i <= 5e6; ++i) sum[i] = (sum[i - 1] + 1ll * i * i % MOD * phi[i] % MOD) % MOD;
}
IN ll f1(ll val)
{return val % MOD * val % MOD * (val + 1) % MOD * (val + 1) % MOD * inv4 % MOD;}
IN ll f2(ll val)
{return val % MOD * (val + 1) % MOD * (2 * val % MOD + 1) % MOD * inv6 % MOD;}
IN ll fpow(ll base, ll tim)
{
	ll ret = 1;
	W (tim)
	{
		if (tim & 1)ret = ret * base % MOD;
		base = base * base % MOD, tim >>= 1;
	}
	return ret;
}
ll solve(R ll bd)
{
	if (bd <= 5e6) return sum[bd];
	if (mp.count(bd)) return mp[bd];
	ll ret = f1(bd);
	for (R ll lef = 2, rig; lef <= bd; lef = rig + 1)
	{
		rig = bd / (bd / lef);
		ret -= ((f2(rig) - f2(lef - 1) + MOD) % MOD) * solve(bd / lef) % MOD;
	}
	return mp[bd] = (ret + MOD) % MOD;
}
int main(void)
{
	ll n, ans = 0, bd, mul;
	scanf("%lld%lld", &MOD, &n);
	get();
	inv2 = fpow(2, MOD - 2);
	inv4 = fpow(4, MOD - 2);
	inv6 = fpow(6, MOD - 2);
	for (R ll lef = 1, rig; lef <= n; lef = rig + 1)
	{
		rig = n / (n / lef); bd = n / lef;
		mul = (1 + bd) % MOD * bd % MOD * inv2 % MOD;
		mul = mul * mul % MOD;
		(ans += mul * ((solve(rig) - solve(lef - 1) + MOD) % MOD) % MOD) %= MOD;
	}
	printf("%lld", ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值