『题解』Luogu-P3312 [SDOI2014]数表

43 篇文章 0 订阅
8 篇文章 0 订阅

P3312 [SDOI2014]数表

Description

  • 多测, Q Q Q 组数据。
  • 有一张 n × m n\times m n×m 的数表,其第 i i i 行第 j j j 列( 1 ≤ i ≤ n , 1 ≤ j ≤ m 1\le i\le n, 1\le j\le m 1in,1jm)的数值为能同时整除 i i i j j j 的所有自然数之和。给定 a a a,计算数表中不大于 a a a 的数之和模 2 31 2^{31} 231 的值。
  • 1 ≤ n , m ≤ 1 0 5 , 1 ≤ Q ≤ 2 × 1 0 4 , ∣ a ∣ ≤ 1 0 9 1\le n, m\le 10^5, 1\le Q\le 2\times 10^4, |a| \le 10^9 1n,m105,1Q2×104,a109

Solution

首先模 2 31 2^{31} 231 就直接用 int \text{int} int 存自然溢出,最后和 2 31 − 1 2^{31} - 1 2311 取 & 即可。

因为 k ∣ n ∧ k ∣ m ⇔ k ∣ gcd ⁡ ( n , m ) k\mid n \land k\mid m \Leftrightarrow k\mid \gcd(n, m) knkmkgcd(n,m),所以第 i i i 行第 j j j 列的数是 σ ( gcd ⁡ ( i , j ) ) \sigma(\gcd(i, j)) σ(gcd(i,j))

先不考虑关于 a a a 的限制,则所求为
a n s = ∑ i = 1 n ∑ j = 1 m σ ( gcd ⁡ ( i , j ) ) = ∑ i = 1 n ∑ j = 1 m ∑ d = 1 n σ ( d ) [ gcd ⁡ ( i , j ) = d ] = ∑ d = 1 n σ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ gcd ⁡ ( i , j ) = 1 ] = ∑ d = 1 n σ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ k ∣ gcd ⁡ ( i , j ) μ ( k ) = ∑ d = 1 n σ ( d ) ∑ k = 1 ⌊ n d ⌋ μ ( k ) ⌊ n d k ⌋ ⌊ m d k ⌋ = ∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ ∑ d ∣ T σ ( d ) μ ( T d ) \begin{aligned} ans & = \sum_{i = 1}^n \sum_{j = 1}^m \sigma(\gcd(i, j)) \\ & = \sum_{i = 1}^n \sum_{j = 1}^m \sum_{d = 1}^n \sigma(d) [\gcd(i, j) = d] \\ & = \sum_{d = 1}^n \sigma(d) \sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor} [\gcd(i, j) = 1] \\ & = \sum_{d = 1}^n \sigma(d) \sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor} \sum_{k\mid \gcd(i, j)} \mu(k) \\ & = \sum_{d = 1}^n \sigma(d) \sum_{k = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \mu(k) \left\lfloor\dfrac{n}{dk}\right\rfloor \left\lfloor\dfrac{m}{dk}\right\rfloor \\ & = \sum_{T = 1}^n \left\lfloor\dfrac{n}{T}\right\rfloor \left\lfloor\dfrac{m}{T}\right\rfloor \sum_{d\mid T} \sigma(d) \mu\left(\dfrac{T}{d}\right) \end{aligned} ans=i=1nj=1mσ(gcd(i,j))=i=1nj=1md=1nσ(d)[gcd(i,j)=d]=d=1nσ(d)i=1dnj=1dm[gcd(i,j)=1]=d=1nσ(d)i=1dnj=1dmkgcd(i,j)μ(k)=d=1nσ(d)k=1dnμ(k)dkndkm=T=1nTnTmdTσ(d)μ(dT)

f ( n ) = ∑ d ∣ n σ ( d ) μ ( T d ) f(n) = \sum_{d\mid n} \sigma(d) \mu\left(\dfrac{T}{d}\right) f(n)=dnσ(d)μ(dT)
如果没有关于 a a a 的限制,我们有 f = σ ∗ μ = Id ⁡ f = \sigma * \mu = \operatorname{Id} f=σμ=Id

加上限制后,需要满足的是 σ ( d ) ≤ a \sigma(d) \le a σ(d)a

我们运用莫队的思想,将所有的 σ \sigma σ 以及 a a a 从小到大排序,枚举 d d d 的倍数加贡献。

我们需要对 f f f 进行单点修改加贡献,数论分块区间查询,直接用树状数组即可。

预处理要枚举 n ln ⁡ n n\ln n nlnn 次,还要修改,所以是 O ( n ln ⁡ n log ⁡ n ) \Omicron(n\ln n\log n) O(nlnnlogn) 的;

查询有 n \sqrt{n} n 个块,块内要 log ⁡ n \log n logn 查区间和,所以是 O ( Q n log ⁡ n ) \Omicron(Q \sqrt{n} \log n) O(Qn logn) 的。

总时间复杂度为 O ( n log ⁡ 2 n + Q n log ⁡ n ) \Omicron(n\log^2 n + Q \sqrt{n} \log n) O(nlog2n+Qn logn)

Code

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

const int MAXN = 1e5 + 5;
const int N = 1e5;
typedef int arr[MAXN];

arr p, mu, sigma, num;
bool vis[MAXN];

pair<int, int> f[MAXN];

void pre()
{
	mu[1] = sigma[1] = 1;
	for (int i = 2; i <= N; i++)
	{
		if (!vis[i])
		{
			p[++p[0]] = i;
			mu[i] = -1;
			sigma[i] = num[i] = 1 + i;
		}
		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;
				sigma[i * p[j]] = sigma[i] / num[i] * (num[i] * p[j] + 1);
				num[i * p[j]] = num[i] * p[j] + 1;
				break;
			}
			mu[i * p[j]] = mu[i] * mu[p[j]];
			sigma[i * p[j]] = sigma[i] * sigma[p[j]];
			num[i * p[j]] = 1 + p[j];
		}
	}
	for (int i = 1; i <= N; i++)
	{
		f[i] = {sigma[i], i};
	}
	sort(f + 1, f + N + 1);
}

arr c;

inline int lowbit(int x)
{
	return x & -x;
}

void update(int x, int k)
{
	for (int i = x; i <= N; i += lowbit(i))
	{
		c[i] += k;
	}
}

int query(int x)
{
	int res = 0;
	for (int i = x; i; i -= lowbit(i))
	{
		res += c[i];
	}
	return res;
}

int query_range(int l, int r)
{
	return query(r) - query(l - 1);
}

struct Query
{
	int id, n, m, a;
	bool operator <(const Query &x)const
	{
		return x.a > a;
	}
}q[MAXN];

arr ans;

int main()
{
	pre();
	int Q;
	scanf("%d", &Q);
	for (int i = 1; i <= Q; i++)
	{
		int n, m, a;
		scanf("%d%d%d", &n, &m, &a);
		q[i] = Query{i, n, m, a};
	}
	sort(q + 1, q + Q + 1);
	for (int t = 1, i = 0; t <= Q; t++)
	{
		int a = q[t].a, val;
		while ((val = f[i + 1].first) <= a)
		{
			int d = f[++i].second;
			for (int k = 1; d * k <= N; k++)
			{
				update(d * k, val * mu[k]);
			}
		}
		int id = q[t].id, n = q[t].n, m = q[t].m;
		if (n > m)
		{
			swap(n, m);
		}
		for (int l = 1, r; l <= n; l = r + 1)
		{
			int k1 = n / l, k2 = m / l;
			r = min(n / k1, m / k2);
			ans[id] += k1 * k2 * query_range(l, r);
		}
	}
	for (int i = 1; i <= Q; i++)
	{
		printf("%d\n", ans[i] & 2147483647);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值