[Luogu P3312] [BZOJ 3529] [SDOI2014]数表

洛谷传送门
BZOJ传送门

题目描述

有一张 N ∗ m N*m Nm的数表,其第 i i i行第 j j j列( 1 ≤ i ≤ n , 1 ≤ j ≤ m 1 \le i \le n,1 \le j \le m 1in1jm)的数值为能同时整除 i i i j j j的所有自然数之和。给定 a a a,计算数表中不大于 a a a的数之和。

输入输出格式

输入格式:

输入包含多组数据。

输入的第一行一个整数 Q Q Q表示测试点内的数据组数

接下来 Q Q Q行,每行三个整数 n n n m m m a ( ∣ a ∣ ≤ 1 0 9 ) a(|a| \le 10^9) a(a109)描述一组数据。

输出格式:

对每组数据,输出一行一个整数,表示答案模 2 31 2^{31} 231的值。

输入输出样例

输入样例#1:
2
4 4 3
10 10 5
输出样例#1:
20
148

说明

1 ≤ N . m ≤ 1 0 5 1 \le N.m \le 10^5 1Nm105 1 ≤ Q ≤ 2 ∗ 1 0 4 1 \le Q \le 2*10^4 1Q2104

解题分析

还是先大力推式子:
∑ i = 1 n ∑ j = 1 m σ 1 ( g c d ( i , j ) ) [ σ 1 ( g c d ( i , j ) ) &lt; a ] = ∑ d = 1 n σ 1 ( d ) [ σ 1 ( d ) &lt; a ] ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] = ∑ d = 1 n σ 1 ( d ) [ σ 1 ( d ) &lt; a ] ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ p ∣ g c d ( i , j ) μ ( p ) = ∑ d = 1 n σ 1 ( d ) [ σ 1 ( d ) &lt; a ] ∑ p = 1 ⌊ n d ⌋ μ ( p ) ⌊ n d p ⌋ ⌊ m d p ⌋ \sum_{i=1}^{n}\sum_{j=1}^{m}\sigma_1(gcd(i,j))[\sigma_1(gcd(i,j))&lt;a] \\ =\sum_{d=1}^n\sigma_1(d)[\sigma_1(d)&lt;a]\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=d] \\ =\sum_{d=1}^n\sigma_1(d)[\sigma_1(d)&lt;a]\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{p|gcd(i,j)}\mu(p) \\ =\sum_{d=1}^n\sigma_1(d)[\sigma_1(d)&lt;a]\sum_{p=1}^{\lfloor\frac{n}{d}\rfloor}\mu(p)\lfloor\frac{n}{dp}\rfloor\lfloor\frac{m}{dp}\rfloor i=1nj=1mσ1(gcd(i,j))[σ1(gcd(i,j))<a]=d=1nσ1(d)[σ1(d)<a]i=1nj=1m[gcd(i,j)=d]=d=1nσ1(d)[σ1(d)<a]i=1dnj=1dmpgcd(i,j)μ(p)=d=1nσ1(d)[σ1(d)<a]p=1dnμ(p)dpndpm
套路地设 T = d p T=dp T=dp, 那么:
∑ d = 1 n σ 1 ( d ) [ σ 1 ( d ) &lt; a ] ∑ p = 1 ⌊ n d ⌋ μ ( p ) ⌊ n d p ⌋ ⌊ m d p ⌋ = ∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ ∑ d ∣ T σ 1 ( d ) [ σ 1 ( d ) &lt; a ] μ ( T d ) \sum_{d=1}^n\sigma_1(d)[\sigma_1(d)&lt;a]\sum_{p=1}^{\lfloor\frac{n}{d}\rfloor}\mu(p)\lfloor\frac{n}{dp}\rfloor\lfloor\frac{m}{dp}\rfloor \\ =\sum_{T=1}^{n}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{d|T}\sigma_1(d)[\sigma_1(d)&lt;a]\mu(\frac{T}{d}) d=1nσ1(d)[σ1(d)<a]p=1dnμ(p)dpndpm=T=1nTnTmdTσ1(d)[σ1(d)<a]μ(dT)
然后我们发现似乎这个玩意资瓷我们更改 a a a的操作? 设 G ( T ) = ∑ d ∣ T σ 1 ( d ) [ σ 1 ( d ) &lt; a ] μ ( T d ) G(T)=\sum_{d|T}\sigma_1(d)[\sigma_1(d)&lt;a]\mu(\frac{T}{d}) G(T)=dTσ1(d)[σ1(d)<a]μ(dT),只要我们离线询问, 然后按 a a a排序,并把 σ 1 ( i ) \sigma_1(i) σ1(i)也排个序, 每次更新可用的 G ( T ) G(T) G(T)的值, 然后用 B I T BIT BIT维护区间和, 套上下底分块即可。

代码如下:

#include <cstdio>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <climits>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
#define ll long long
#define lbt(i) ((i) & (-(i)))
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int pcnt, T;
int miu[MX], pri[MX], sig1[MX];
bool npr[MX];
ll G[MX], ans[MX];
const unsigned int MOD = (1u << 31);
struct INFO {int n, m, lim, id;} que[MX];
struct DAT {int pos, val;} dat[MX];
IN bool operator < (const INFO &x, const INFO &y) {return x.lim < y.lim;}
IN bool operator < (const DAT &x, const DAT &y) {return x.val < y.val;}
IN void add(R int pos, R int val) {for (; pos <= 1e5; pos += lbt(pos)) (G[pos] += val) %= MOD;}
IN ll query(R int pos)
{
	ll ret = 0;
	for (; pos; pos -= lbt(pos)) ret += G[pos];
	return ret;
}
IN void modify(R int pos, R int val)
{
	for (R int i = 1; i * pos <= 1e5; i++)
	add(pos * i, val * miu[i]);
}
IN ll getit(R int lef, R int rig) {return query(rig) - query(lef - 1);}
void get()
{
	miu[1] = 1; int tar;
	for (R int i = 2; i <= 1e5; ++i)
	{
		if (!npr[i]) pri[++pcnt] = i, miu[i] = -1;
		for (R int j = 1; j <= pcnt; ++j)
		{
			tar = i * pri[j];
			if (tar > 1e5) break;
			npr[tar] = true;
			if (!(i % pri[j])) {miu[tar] = 0; break;}
			miu[tar] = -miu[i];
		}
	}
	for (R int i = 1; i <= 1e5; ++i)
	for (R int j = i; j <= 1e5; j += i)
	sig1[j] += i;
	for (R int i = 1; i <= 1e5; ++i) dat[i] = {i, sig1[i]};
}
signed main(void)
{
	in(T); get();
	for (R int i = 1; i <= T; ++i)
	in(que[i].n), in(que[i].m), in(que[i].lim), que[i].id = i;
	std::sort(dat + 1, dat + 1 + 100000);
	std::sort(que + 1, que + 1 + T);
	for (R int i = 1, cur = 1; i <= T; ++i)
	{
		W (cur <= 1e5 && dat[cur].val <= que[i].lim) modify(dat[cur].pos, dat[cur].val), ++cur;
		if (que[i].n > que[i].m) std::swap(que[i].n, que[i].m);
		for (R int lef = 1, rig; lef <= que[i].n; lef = rig + 1)
		{
			rig = std::min(que[i].n / (que[i].n / lef), que[i].m / (que[i].m / lef));
			ans[que[i].id] += 1ll * (que[i].n / lef) * (que[i].m / lef) * getit(lef, rig);
		}
	}
	for (R int i = 1; i <= T; ++i) printf("%lld\n", (ans[i] % MOD + MOD) % MOD);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值