『题解』Luogu-P5572 [CmdOI2019]简单的数论题

P5572 [CmdOI2019]简单的数论题

思路基本同 P4240 毒瘤之神的考验

Description

  • 多测, T T T 组数据。

  • 给定整数 n , m n, m n,m,请求出
    [ ∑ i = 1 n ∑ j = 1 m φ ( lcm ⁡ ( i , j ) gcd ⁡ ( i , j ) ) ]   m o d   23333 \left[\sum_{i = 1}^n \sum_{j = 1}^m \varphi\left(\dfrac{\operatorname{lcm}(i, j)}{\gcd(i, j)} \right) \right] \bmod 23333 [i=1nj=1mφ(gcd(i,j)lcm(i,j))]mod23333

  • T ≤ 3 × 1 0 4 , m ≤ n ≤ 5 × 1 0 4 T\le 3\times 10^4, m\le n\le 5\times 10^4 T3×104,mn5×104

Solution

不妨设 n ≤ m n\le m nm
a n s = ∑ d = 1 n ∑ i = 1 n ∑ j = 1 m φ ( i j d 2 ) [ gcd ⁡ ( i , j ) = d ] = ∑ d = 1 n ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ φ ( i j ) [ gcd ⁡ ( i , j ) = 1 ] = ∑ d = 1 n ∑ i = 1 ⌊ n d ⌋ φ ( i ) ∑ j = 1 ⌊ m d ⌋ φ ( j ) [ gcd ⁡ ( i , j ) = 1 ] = ∑ d = 1 n ∑ k = 1 ⌊ n d ⌋ μ ( k ) ∑ i = 1 ⌊ n d k ⌋ φ ( i k ) ∑ j = 1 ⌊ m d k ⌋ φ ( j k ) = ∑ T = 1 n ∑ k ∣ T μ ( k ) ∑ i = 1 ⌊ n T ⌋ φ ( i k ) ∑ j = 1 ⌊ m T ⌋ φ ( j k ) \begin{aligned} ans & = \sum_{d = 1}^n \sum_{i = 1}^n \sum_{j = 1}^m \varphi\left(\dfrac{ij}{d^2} \right) [\gcd(i, j) = d] \\ & = \sum_{d = 1}^n \sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor} \varphi(ij) [\gcd(i, j) = 1] \\ & = \sum_{d = 1}^n \sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \varphi(i) \sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor} \varphi(j) [\gcd(i, j) = 1] \\ & = \sum_{d = 1}^n \sum_{k = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \mu(k) \sum_{i = 1}^{\left\lfloor\frac{n}{dk}\right\rfloor} \varphi(ik) \sum_{j = 1}^{\left\lfloor\frac{m}{dk}\right\rfloor} \varphi(jk) \\ & = \sum_{T = 1}^n \sum_{k\mid T} \mu(k) \sum_{i = 1}^{\left\lfloor\frac{n}{T}\right\rfloor} \varphi(ik) \sum_{j = 1}^{\left\lfloor\frac{m}{T}\right\rfloor} \varphi(jk) \end{aligned} ans=d=1ni=1nj=1mφ(d2ij)[gcd(i,j)=d]=d=1ni=1dnj=1dmφ(ij)[gcd(i,j)=1]=d=1ni=1dnφ(i)j=1dmφ(j)[gcd(i,j)=1]=d=1nk=1dnμ(k)i=1dknφ(ik)j=1dkmφ(jk)=T=1nkTμ(k)i=1Tnφ(ik)j=1Tmφ(jk)
套路地设出来
f ( k , n ) = ∑ i = 1 n φ ( i k ) f(k, n) = \sum_{i = 1}^n \varphi(ik) f(k,n)=i=1nφ(ik)
那么
a n s = ∑ T = 1 n ∑ k ∣ T μ ( k ) f ( k , ⌊ n T ⌋ ) f ( k , ⌊ m T ⌋ ) ans = \sum_{T = 1}^n \sum_{k\mid T} \mu(k) f\left(k, \left\lfloor\dfrac{n}{T}\right\rfloor \right) f\left(k, \left\lfloor\dfrac{m}{T}\right\rfloor \right) ans=T=1nkTμ(k)f(k,Tn)f(k,Tm)
又有递推式
f ( k , n ) = f ( k , n − 1 ) + φ ( n k ) f(k, n) = f(k, n - 1) + \varphi(nk) f(k,n)=f(k,n1)+φ(nk)
f f f 可以在 Θ ( n ln ⁡ n ) \Theta(n\ln n) Θ(nlnn) 时间内预处理。

但是这个 f f f 带一个 k k k,无法整除分块,只能暴力,所以查询是 O ( T ∑ i = 1 n n i ) ≃ O ( T n ln ⁡ n ) \Omicron(T\sum\limits_{i = 1}^n \dfrac{n}{i}) \simeq \Omicron(T n\ln n) O(Ti=1nin)O(Tnlnn) 的。


我们想要整除分块,那就弄个前缀和。

把整个表示出来
g ( n , m , l ) = ∑ i = 1 l ∑ k ∣ i μ ( k ) f ( k , n ) f ( k , m ) g(n, m, l) = \sum_{i = 1}^l \sum_{k\mid i} \mu(k) f(k, n) f(k, m) g(n,m,l)=i=1lkiμ(k)f(k,n)f(k,m)
那么整除分块大概长这样
g ( ⌊ n l ⌋ , ⌊ m l ⌋ , r ) − g ( ⌊ n l ⌋ , ⌊ m l ⌋ , l − 1 ) g\left(\left\lfloor\dfrac{n}{l}\right\rfloor, \left\lfloor\dfrac{m}{l}\right\rfloor, r \right) - g\left(\left\lfloor\dfrac{n}{l}\right\rfloor, \left\lfloor\dfrac{m}{l}\right\rfloor, l - 1 \right) g(ln,lm,r)g(ln,lm,l1)
同样有递推式
g ( n , m , l ) = g ( n , m , l − 1 ) + ∑ k ∣ l μ ( k ) f ( k , n ) f ( k , m ) g(n, m, l) = g(n, m, l - 1) + \sum_{k\mid l} \mu(k) f(k, n) f(k, m) g(n,m,l)=g(n,m,l1)+klμ(k)f(k,n)f(k,m)
预处理是
O ( n ∑ i = 1 n ( ∑ j = 1 n i n j ) + n i ) ≃ O ( n 3 ) \Omicron\left(n \sum_{i = 1}^n \left(\sum_{j = 1}^{\frac{n}{i}} \dfrac{n}{j}\right) + \dfrac{n}{i} \right) \simeq \Omicron(n^3) Oni=1nj=1injn+inO(n3)
T L E + M L E \rm TLE + MLE TLE+MLE

但是它能够做到十分优秀的 O ( T n ) \Omicron(T \sqrt{n}) O(Tn ) 回答。


如果要将两种方法结合,那么就是大家喜闻乐见的 根号分治 了。

(其实题目背景已经有提示了)

A:“数论,分块。”

k k k 为阈值,表示预处理 g ( n , m , l ) g(n, m, l) g(n,m,l) n , m n, m n,m 只处理到 k k k

那么计算时对于 l ≤ ⌊ n k ⌋ l \le \left\lfloor\dfrac{n}{k}\right\rfloor lkn 的直接暴力;对于 l > ⌊ n k ⌋ l > \left\lfloor\dfrac{n}{k}\right\rfloor l>kn 的部分,有 ⌊ n l ⌋ ≤ k \left\lfloor\dfrac{n}{l}\right\rfloor \le k lnk,直接用预处理过的 g g g

Code

//18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#include <vector> 
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;

const int MAXN = 5e4 + 5;
const int N = 5e4;
const int MAXK = 155;
const int K = 150;
const int MOD = 23333;
typedef int arr[MAXN];
int add(int a, int b) {return (a + b) % MOD;}
int sub(int a, int b) {return (a - b + MOD) % MOD;}
int mul(int a, int b) {return (ll)a * b % MOD;}

arr p, mu, phi;
bool vis[MAXN];
vector<int> f[MAXN], g[MAXK][MAXK];

void pre()
{
	mu[1] = phi[1] = 1;
	for (int i = 2; i <= N; i++)
	{
		if (!vis[i])
		{
			p[++p[0]] = i;
			mu[i] = MOD - 1;
			phi[i] = (i - 1) % MOD;
		}
		for (int j = 1; j <= p[0] && i * p[j] <= N; j++)
		{
			vis[i * p[j]] = true;
			if (i % p[j] == 0)
			{
				mu[i * p[j]] = 0;
				phi[i * p[j]] = mul(phi[i], p[j]);
				break;
			}
			mu[i * p[j]] = mul(mu[i], mu[p[j]]);
			phi[i * p[j]] = mul(phi[i], phi[p[j]]);
		}
	}
	
	for (int k = 1; k <= N; k++)
	{
		f[k].resize(N / k + 5);
		for (int n = 1; n <= N / k; n++)
		{
			f[k][n] = add(f[k][n - 1], phi[n * k]);
		}
	}
	
	for (int n = 1; n <= K; n++)
	{
		for (int m = n; m <= K; m++)
		{
			g[n][m].resize(N / m + 5);
			for (int i = 1; i <= N / m; i++)
			{
				int wjy = mul(mu[i], mul(f[i][n], f[i][m]));
				for (int j = 1; j <= N / m / i; j++)
				{
					g[n][m][i * j] = add(g[n][m][i * j], wjy);
				}
			}
			for (int l = 1; l <= N / m; l++)
			{
				g[n][m][l] = add(g[n][m][l], g[n][m][l - 1]);
			}
		}
	}
}

int block(int n, int m)
{
	if (n > m)
	{
		swap(n, m);
	}
	int res = 0, xsy = m / K;
	for (int i = 1; i <= xsy; i++)
	{
		for (int j = 1; j <= xsy / i; j++)
		{
			int t = i * j;
			res = add(res, mul(mu[i], mul(f[i][n / t], f[i][m / t])));
		}
	}
	for (int l = xsy + 1, r; l <= n; l = r + 1)
	{
		int k1 = n / l, k2 = m / l;
		r = min(n / k1, m / k2);
		res = add(res, sub(g[k1][k2][r], g[k1][k2][l - 1]));
	}
	return res;
}

int main()
{
	pre();
	int t;
	scanf("%d", &t);
	while (t--)
	{
		int n, m;
		scanf("%d%d", &n, &m);
		printf("%d\n", block(n, m));
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于洛谷上的p1036题目,我们可以使用Python来解决。下面是一个可能的解法: ```python def dfs(nums, target, selected_nums, index, k, sum): if k == 0 and sum == target: return 1 if index >= len(nums) or k <= 0 or sum > target: return 0 count = 0 for i in range(index, len(nums)): count += dfs(nums, target, selected_nums + [nums[i]], i + 1, k - 1, sum + nums[i]) return count if __name__ == "__main__": n, k = map(int, input().split()) nums = list(map(int, input().split())) target = int(input()) print(dfs(nums, target, [], 0, k, 0)) ``` 在这个解法中,我们使用了深度优先搜索(DFS)来找到满足要求的数列。通过递归的方式,我们遍历了所有可能的数字组合,并统计满足条件的个数。 首先,我们从给定的n和k分别表示数字个数和需要选取的数字个数。然后,我们输入n个数字,并将它们存储在一个列表nums中。接下来,我们输入目标值target。 在dfs函数中,我们通过迭代index来选择数字,并更新选取的数字个数k和当前总和sum。如果k等于0且sum等于target,我们就找到了一个满足条件的组合,返回1。如果index超出了列表长度或者k小于等于0或者sum大于target,说明当前组合不满足要求,返回0。 在循环中,我们不断递归调用dfs函数,将选取的数字添加到selected_nums中,并将index和k更新为下一轮递归所需的值。最终,我们返回所有满足条件的组合个数。 最后,我们在主程序中读入输入,并调用dfs函数,并输出结果。 这是一种可能的解法,但不一定是最优解。你可以根据题目要求和测试数据进行调试和优化。希望能对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值