终于写完13题的题解了
用时
23
23
23 天
以此帖纪念洛谷红名
题单:
题单 \Huge \color{red}{题单} 题单
- P2568 GCD
- P2398 GCD SUM
- P1447 [NOI2010] 能量采集
- P2522 [HAOI2011]Problem b
- P4318 完全平方数
- P2257 YY的GCD
- P1829 [国家集训队]Crash的数字表格 / JZPTAB
- P3327 [SDOI2015]约数个数和
- P4449 于神之怒加强版
- P3312 [SDOI2014]数表
- P3704 [SDOI2017]数字表格
- CF1780F Three Chairs
- CF235E Number Challenge
T1 P2568 GCD
题目描述
给定正整数 n n n,求 1 ≤ x , y ≤ n 1\le x,y\le n 1≤x,y≤n 且 gcd ( x , y ) \gcd(x,y) gcd(x,y) 为素数的数对 ( x , y ) (x,y) (x,y) 有多少对。
题目简化:求 ∑ p ∈ p r i m e s ∑ i = 1 n ∑ j = 1 n [ gcd ( i , j ) = = p ] \sum_{p \in primes} \sum_{i=1}^{n} \sum_{j=1}^{n} [\gcd(i, j) == p] ∑p∈primes∑i=1n∑j=1n[gcd(i,j)==p]
转化为 ∑ p ∈ p r i m e s ∑ i = 1 ⌊ n p ⌋ ∑ j = 1 ⌊ n p ⌋ [ gcd ( i , j ) = = 1 ] \sum_{p \in primes} \sum_{i=1}^{\lfloor \frac{n}{p} \rfloor} \sum_{j=1}^{\lfloor \frac{n}{p} \rfloor} [\gcd(i, j) == 1] ∑p∈primes∑i=1⌊pn⌋∑j=1⌊pn⌋[gcd(i,j)==1]
a n s = ∑ i = 1 ⌊ n p ⌋ ϕ ( i ) ans = \sum_{i=1}^{\lfloor \frac{n}{p} \rfloor} \phi(i) ans=∑i=1⌊pn⌋ϕ(i)
#include <bits/stdc++.h>
using namespace std;
const long long N = 1e7 + 10;
long long euler[N], primes[N], sum[N], cnt;
bool st[N];
long long n;
void get_eulers(long long n) // 线性筛法求1~n的欧拉函数
{
euler[1] = 1;
for (long long i = 2; i <= n; i ++ ) {
if (!st[i]) {
primes[ ++cnt ] = i;
euler[i] = i - 1;
}
for (long long j = 1; primes[j] <= n / i; j ++ ) {
long long t = primes[j] * i;
st[t] = true;
if (i % primes[j] == 0) {
euler[t] = euler[i] * primes[j];
break;
}
euler[t] = euler[i] * (primes[j] - 1);
}
}
}
int main() {
scanf("%d", &n);
get_eulers(n);
for (long long i = 1; i <= n; i++) sum[i] = sum[i - 1] + euler[i];
long long ans = 0;
for (long long i = 1; i <= cnt && primes[i] <= n; i++) ans += 1ll * (sum[n / primes[i]] * 2) - 1;
cout << ans;
return 0;
}
T2 P2398 GCD SUM
题目描述
求
∑ i = 1 n ∑ j = 1 n gcd ( i , j ) \sum_{i=1}^n \sum_{j=1}^n \gcd(i, j) i=1∑nj=1∑ngcd(i,j)
从 gcd \gcd gcd 入手。
设 f k f_k fk 为 gcd ( i , j ) = = k \gcd(i,j) == k gcd(i,j)==k 的个数。
则 a n s = ∑ i = 1 n f i × i ans = \sum_{i=1}^{n} f_i \times i ans=∑i=1nfi×i
但这样不好算,因此引入 g g g 数组。
设 g k g_k gk 为 k ∣ g c d ( i , j ) k | gcd(i,j) k∣gcd(i,j) 的个数。
由 g g g 的定义, g k = ⌊ n k ⌋ 2 g_k = \lfloor \frac{n}{k} \rfloor ^ 2 gk=⌊kn⌋2
这两个 g k g_k gk 相等,因此可以这么表示 f k f_k fk:
f k = ⌊ n k ⌋ 2 − f 2 k − f 3 k − . . . . . . f_k = \lfloor \frac{n}{k} \rfloor ^ 2 - f_{2k} - f{3k} - ...... fk=⌊kn⌋2−f2k−f3k−......
求 f f f 数组方便多了,可以逆序推导 f f f,最后统计答案。
#include <bits/stdc++.h>
using namespace std;
const long long N = 1e5 + 10;
long long n, f[N];
long long ans = 0;
int main() {
scanf("%d", &n);
for (long long i = n; i > 0; i--) {
long long x = i; f[i] = (n / i) * (n / i);
while (x + i <= n) {
x += i;
f[i] -= f[x];
}
ans += f[i] * i;
}
cout << ans;
return 0;
}
T3 P1447 [NOI2010] 能量采集
题目描述
栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可以采集太阳光的能量。在这些植物采集能量后,栋栋再使用一个能量汇集机器把这些植物采集到的能量汇集到一起。
栋栋的植物种得非常整齐,一共有 n n n 列,每列有 m m m 棵,植物的横竖间距都一样,因此对于每一棵植物,栋栋可以用一个坐标 ( x , y ) (x, y) (x,y) 来表示,其中 x x x 的范围是 1 1 1 至 n n n, y y y 的范围是 1 1 1 至 m m m,表示是在第 x x x 列的第 y y y 棵。
由于能量汇集机器较大,不便移动,栋栋将它放在了一个角上,坐标正好是 ( 0 , 0 ) (0, 0) (0,0)。
能量汇集机器在汇集的过程中有一定的能量损失。如果一棵植物与能量汇集机器连接而成的线段上有 k k k 棵植物,则能量的损失为 2 k + 1 2k + 1 2k+1。例如,当能量汇集机器收集坐标为 ( 2 , 4 ) (2, 4) (2,4) 的植物时,由于连接线段上存在一棵植物 ( 1 , 2 ) (1, 2) (1,2),会产生 3 3 3 的能量损失。注意,如果一棵植物与能量汇集机器连接的线段上没有植物,则能量损失为 1 1 1。现在要计算总的能量损失。
下面给出了一个能量采集的例子,其中 n = 5 n = 5 n=5, m = 4 m = 4 m=4,一共有 20 20 20 棵植物,在每棵植物上标明了能量汇集机器收集它的能量时产生的能量损失。
在这个例子中,总共产生了 36 36 36 的能量损失。
题意转化:求
( 2 × ∑ i = 1 n ∑ j = 1 m gcd ( i , j ) ) − n × m \bigg( 2 \times \sum_{i=1}^{n} \sum_{j=1}^{m} \gcd(i,j) \bigg) - n \times m (2×∑i=1n∑j=1mgcd(i,j))−n×m。
引理:欧拉反演。
n = ∑ d ∣ n ϕ ( d ) n = \sum_{d|n} \phi(d) n=d∣n∑ϕ(d)
现在重点只考虑 ∑ i = 1 n ∑ j = 1 m gcd ( i , j ) \sum_{i=1}^{n} \sum_{j=1}^{m} \gcd(i,j) ∑i=1n∑j=1mgcd(i,j)。
转化为: ∑ i = 1 n ∑ j = 1 m ∑ d ∣ g c d ( i , j ) ϕ ( d ) \sum_{i=1}^{n} \sum_{j=1}^{m} \sum_{d|gcd(i,j)} \phi(d) ∑i=1n∑j=1m∑d∣gcd(i,j)ϕ(d)。
= ∑ d = 1 n ϕ ( d ) ∑ i = 1 n ∑ j = 1 m [ d ∣ g c d ( i , j ) ] =\sum_{d=1}^{n} \phi(d) \sum_{i=1}^{n} \sum_{j=1}^{m} [d|gcd(i,j)] =∑d=1nϕ(d)∑i=1n∑j=1m[d∣gcd(i,j)]
= ∑ d = 1 n ϕ ( d ) ⌊ n d ⌋ ⌊ m d ⌋ =\sum_{d=1}^{n} \phi(d) \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor =∑d=1nϕ(d)⌊dn⌋⌊dm⌋
直接线性筛 ϕ \phi ϕ 就行了。
#include <bits/stdc++.h>
using namespace std;
const long long N = 1e7 + 10;
long long euler[N], primes[N], sum[N], cnt;
bool st[N];
long long n, m;
void get_eulers(long long n) // 线性筛法求1~n的欧拉函数
{
euler[1] = 1;
for (long long i = 2; i <= n; i ++ ) {
if (!st[i]) {
primes[ ++cnt ] = i;
euler[i] = i - 1;
}
for (long long j = 1; primes[j] <= n / i; j ++ ) {
long long t = primes[j] * i;
st[t] = true;
if (i % primes[j] == 0) {
euler[t] = euler[i] * primes[j];
break;
}
euler[t] = euler[i] * (primes[j] - 1);
}
}
}
int main() {
scanf("%d%d", &n, &m);
get_eulers(n);
for (long long i = 1; i <= n; i++) sum[i] = sum[i - 1] + euler[i];
long long ans = 0;
for (int d = 1; d <= n; d++) {
ans += euler[d] * (n / d) * (m / d);
}
cout << 2 * ans - n * m;
return 0;
}
T4 P2522 [HAOI2011]Problem b
题目描述
对于给出的 n n n 个询问,每次求有多少个数对 ( x , y ) (x,y) (x,y),满足 a ≤ x ≤ b a \le x \le b a≤x≤b, c ≤ y ≤ d c \le y \le d c≤y≤d,且 gcd ( x , y ) = k \gcd(x,y) = k gcd(x,y)=k, gcd ( x , y ) \gcd(x,y) gcd(x,y) 函数为 x x x 和 y y y 的最大公约数。
简化题意:求
∑ i = a b ∑ j = c d [ gcd ( i , j ) = = k ] \sum_{i=a}^{b} \sum_{j=c}^{d} [\gcd(i,j) == k] i=a∑bj=c∑d[gcd(i,j)==k]
这题有一个类似二维前缀和的思想,或者说是容斥。
A n s = s o l v e ( ( 1 , b ) , ( 1 , d ) ) − s o l v e ( ( 1 , b ) , ( 1 , c − 1 ) ) − s o l v e ( ( 1 , a − 1 ) , ( 1 , d ) ) + s o l v e ( ( 1 , a − 1 ) , ( 1 , c − 1 ) ) Ans = solve((1,b),(1,d))-solve((1,b),(1,c-1))-solve((1,a-1),(1,d))+solve((1,a-1),(1,c-1)) Ans=solve((1,b),(1,d))−solve((1,b),(1,c−1))−solve((1,a−1),(1,d))+solve((1,a−1),(1,c−1))
至此,完成了 1 − n , 1 − m 1-n,1-m 1−n,1−m的转化。只要求解这个子问题就行了。
P3455 [POI2007]ZAP-Queries
这题即是 T4 的子问题。
题目描述
密码学家正在尝试破解一种叫 BSA 的密码。
他发现,在破解一条消息的同时,他还需要回答这样一种问题:
给出 a , b , d a,b,d a,b,d,求满足 1 ≤ x ≤ a 1 \leq x \leq a 1≤x≤a, 1 ≤ y ≤ b 1 \leq y \leq b 1≤y≤b,且 gcd ( x , y ) = d \gcd(x,y)=d gcd(x,y)=d 的二元组 ( x , y ) (x,y) (x,y) 的数量。
因为要解决的问题实在太多了,他便过来寻求你的帮助。
设 f ( k ) = ∑ i = 1 a ∑ i = 1 b [ gcd ( i , j ) = = k ] f(k) = \sum_{i=1}^{a} \sum_{i=1}^{b} [\gcd(i,j)==k] f(k)=∑i=1a∑i=1b[gcd(i,j)==k]
设 g ( k ) = ∑ n ∣ k f ( k ) = ⌊ a n ⌋ ⌊ b n ⌋ g(k) = \sum_{n|k} f(k) = \lfloor \frac{a}{n} \rfloor \lfloor \frac{b}{n} \rfloor g(k)=∑n∣kf(k)=⌊na⌋⌊nb⌋
根据莫反有: f ( k ) = ∑ n ∣ k μ ( ⌊ k n ⌋ ) F ( k ) f(k) = \sum_{n|k} \mu(\lfloor \frac{k}{n} \rfloor) F(k) f(k)=∑n∣kμ(⌊nk⌋)F(k)
根据题目, A n s = f ( d ) Ans = f(d) Ans=f(d)。
把 A n s Ans Ans 用莫反的式子表示出来:
A n s = ∑ d ∣ k μ ( ⌊ k d ⌋ ) F ( k ) Ans = \sum_{d|k} \mu(\lfloor \frac{k}{d} \rfloor) F(k) Ans=∑d∣kμ(⌊dk⌋)F(k)
A n s = ∑ d ∣ k μ ( ⌊ k d ⌋ ) ⌊ a k ⌋ ⌊ b k ⌋ Ans = \sum_{d|k} \mu(\lfloor \frac{k}{d} \rfloor) \lfloor \frac{a}{k} \rfloor \lfloor \frac{b}{k} \rfloor Ans=∑d∣kμ(⌊dk⌋)⌊ka⌋⌊kb⌋
设 t = b k t=\frac{b}{k} t=kb,改为枚举 t t t,则:
A n s = ∑ t = 1 min ( ⌊ a d ⌋ , ⌊ b d ⌋ ) μ ( t ) ⌊ a t d ⌋ ⌊ b t d ⌋ Ans = \sum_{t=1}^{\min(\lfloor \frac{a}{d} \rfloor , \lfloor \frac{b}{d} \rfloor)} \mu(t) \lfloor \frac{a}{td} \rfloor \lfloor \frac{b}{td} \rfloor Ans=∑t=1min(⌊da⌋,⌊db⌋)μ(t)⌊tda⌋⌊tdb⌋
至此,该子问题得到了解决,已经是 O ( N ) O(N) O(N) 的效率了。
但是由于多组数据,需要采用数论分块优化到 O ( N ) O(\sqrt N) O(N)。
子问题代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int mu[N], prime[N], tot = 0, sum[N];
bool st[N];
int read(){
int x = 0; int zf = 1; char ch = ' ';
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
inline void init(int n) {
mu[1] = 1;
for (register int i = 2; i <= n; i++) {
if (!st[i]) prime[++tot] = i, mu[i] = -1;
for (register int j = 1; j <= tot && prime[j] * i <= n; j++) {
st[prime[j] * i] = 1;
if (i % prime[j] != 0) mu[i * prime[j]] = -mu[i];
else { mu[prime[j] * i] = 0; break; }
}
}
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
}
int k;
int f(int x, int y) {
int res = 0;
for (int l = 1, r; l <= min(x, y); l = r + 1) {
r = min(x / (x / l), y / (y / l));
res += (sum[r] - sum[l - 1]) * ((x / k) / l) * ((y / k) / l);
}
return res;
}
int main() {
init(50000);
int T; T = read();
while (T--) {
static int a, b; a = read(), b = read(), k = read();
printf("%lld\n", f(a, b));
}
return 0;
}
原问题只需要像开头说的一样容斥解决。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int mu[N], prime[N], tot = 0, sum[N];
bool st[N];
int read(){
int x = 0; int zf = 1; char ch = ' ';
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
inline void init(int n) {
mu[1] = 1;
for (register int i = 2; i <= n; i++) {
if (!st[i]) prime[++tot] = i, mu[i] = -1;
for (register int j = 1; j <= tot && prime[j] * i <= n; j++) {
st[prime[j] * i] = 1;
if (i % prime[j] != 0) mu[i * prime[j]] = -mu[i];
else { mu[prime[j] * i] = 0; break; }
}
}
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
}
int k;
int f(int x, int y) {
int res = 0;
for (int l = 1, r; l <= min(x, y); l = r + 1) {
r = min(x / (x / l), y / (y / l));
res += (sum[r] - sum[l - 1]) * ((x / k) / l) * ((y / k) / l);
}
return res;
}
int main() {
init(50000);
int T; T = read();
while (T--) {
static int a, b, c, d; a = read(), b = read(), c = read(), d = read(), k = read();
printf("%lld\n", f(b, d) - f(b, c - 1) - f(a - 1, d) + f(a - 1, c - 1));
}
return 0;
}
T5 P4318 完全平方数
题目描述
小 X 自幼就很喜欢数。但奇怪的是,他十分讨厌完全平方数。他觉得这些数看起来很令人难受。由此,他也讨厌所有是完全平方数的正整数倍的数。然而这丝毫不影响他对其他数的热爱。
这天是小X的生日,小 W 想送一个数给他作为生日礼物。当然他不能送一个小X讨厌的数。他列出了所有小X不讨厌的数,然后选取了第 K个数送给了小X。小X很开心地收下了。
然而现在小 W 却记不起送给小X的是哪个数了。你能帮他一下吗?
题意简化:
多组数据,每次求第 K K K 个不含大于 1 1 1 的完全平方因子的正整数。
由于当时正好学杜教筛,因此就用了杜教筛的方法。
详见 OI-Wiki
杜教筛可以在低于线性时间内处理数论函数的前缀和。
个人认为就是个记忆化+预处理优化。
回到原题,平方因子和莫比乌斯函数有点关系。
题目要求第 K K K 个满足条件的数,因此有: ∑ i = 1 a n s [ μ ( i ) ≠ 0 ] = = K \sum_{i=1}^{ans} [\mu(i) \neq 0] == K ∑i=1ans[μ(i)=0]==K,并且 μ ( a n s ) ≠ 0 \mu(ans) \neq 0 μ(ans)=0。
μ \mu μ 函数有三种取值: − 1 , 0 , 1 -1,0,1 −1,0,1。 − 1 -1 −1 不是很好处理,要特判,因此直接用 μ 2 ( n ) \mu^2(n) μ2(n) 来代替 μ ( n ) \mu(n) μ(n)。(并不代表这两个等价,只是在此题中用此方法更方便)
设 f ( n ) = μ 2 ( n ) , S ( n ) = ∑ i = 1 n f ( i ) f(n) = \mu^2(n), S(n) = \sum_{i=1}^{n} f(i) f(n)=μ2(n),S(n)=∑i=1nf(i)
因此 S ( a n s ) = = k S(ans) == k S(ans)==k 且 f ( a n s ) ≠ 0 f(ans) \neq 0 f(ans)=0。
杜教筛可以在低于线性时间内处理数论函数的前缀和。
数据范围已经高到 1 0 9 10^9 109 了,而且要处理前缀和的 μ 2 \mu^2 μ2 是一个积性函数。
因此考虑杜教筛。
这里设 g ( n ) = [ n = k 2 , k ∈ N + ] g(n) = [n = k^2, k \in N_{+}] g(n)=[n=k2,k∈N+]。
再考虑卷积: ( f × g ) ( n ) = ∑ d ∣ n g ( d ) × f ( n d ) (f \times g)(n) = \sum_{d | n} {g(d) \times f( \frac{n}{d} )} (f×g)(n)=∑d∣ng(d)×f(dn)
这里 g ( d ) × f ( n d ) g(d) \times f( \frac{n}{d} ) g(d)×f(dn) 只有在 d d d 取 n n n 的最大平方因子时为 1 1 1,否则为 0 0 0。
它的前缀和是很好求的,因为每个数都有最大平方因子(每个数都是 1 1 1 的倍数)。因此 ( f × g ) ( n ) = 1 ( n ) = 1 (f \times g)(n) = 1(n) = 1 (f×g)(n)=1(n)=1。
继续往后推: g ( 1 ) S ( n ) = ∑ i = 1 n ( f × g ) ( i ) − ∑ i = 2 n g ( i ) × S ( ⌊ n i ⌋ ) = n − ∑ i = 2 n g ( i ) × S ( ⌊ n i ⌋ ) g(1)S(n) = \sum_{i=1}^{n} (f \times g)(i) - \sum_{i=2}^{n} g(i) \times S(\lfloor \frac{n}{i} \rfloor) = n - \sum_{i=2}^{n} g(i) \times S(\lfloor \frac{n}{i} \rfloor) g(1)S(n)=∑i=1n(f×g)(i)−∑i=2ng(i)×S(⌊in⌋)=n−∑i=2ng(i)×S(⌊in⌋)
把 g g g 函数去掉,即枚举平方因子:
S ( n ) = n − ∑ i = 2 n g ( i ) × S ( ⌊ n i ⌋ ) = n − ∑ i = 2 n S ( ⌊ n i 2 ⌋ ) S(n) = n - \sum_{i=2}^{n} g(i) \times S(\lfloor \frac{n}{i} \rfloor) = n - \sum_{i=2}^{\sqrt n} S(\lfloor \frac{n}{i^2} \rfloor) S(n)=n−∑i=2ng(i)×S(⌊in⌋)=n−∑i=2nS(⌊i2n⌋)
到这里就可以直接杜教筛了。
#include <bits/stdc++.h>
using namespace std;
const int N = 1005010;
int mu[N], prime[N], sum[N], tot = 0;
bool st[N];
inline void init(int n) {
sum[1] = mu[1] = 1;
for (register long long i = 2; i <= n; i++) {
if (!st[i]) prime[++tot] = i, mu[i] = 1;
for (register int j = 1; j <= tot && prime[j] * i <= n; j++) {
st[prime[j] * i] = 1;
if (i % prime[j] != 0) mu[i * prime[j]] = mu[i];
else { mu[prime[j] * i] = 0; break; }
}
sum[i] = sum[i - 1] + mu[i];
}
}
int T, n;
map<int, int> mp;
int dfs(int x) {
if (x <= N - 3) return sum[x];
if (mp[x]) return mp[x];
int ans = x;
for (int i = 2; i * i <= x; i++) ans -= dfs(x / (i * i));
mp[x] = ans;
return ans;
}
int main() {
init(N - 3);
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
long long l = n, r = 2 * n;
while (l < r) {
long long mid = l + r >> 1;
if (dfs(mid) >= n) r = mid;
else l = mid + 1;
}
printf("%lld\n", r);
}
return 0;
}
T6 P2257 YY的GCD
题目描述
神犇 YY 虐完数论后给傻× kAc 出了一题
给定 N , M N, M N,M,求 1 ≤ x ≤ N 1 \leq x \leq N 1≤x≤N, 1 ≤ y ≤ M 1 \leq y \leq M 1≤y≤M 且 gcd ( x , y ) \gcd(x, y) gcd(x,y) 为质数的 ( x , y ) (x, y) (x,y) 有多少对。
简化题意:求 ∑ i = 1 N ∑ j = 1 M [ gcd ( i , j ) ∈ P r i m e ] \sum_{i=1}^{N} \sum_{j=1}^{M} [\gcd(i,j) \in Prime] ∑i=1N∑j=1M[gcd(i,j)∈Prime]
提出
p
p
p:
∑
p
∈
P
r
i
m
e
∑
i
=
1
N
∑
j
=
1
M
[
gcd
(
i
,
j
)
=
=
p
]
\sum_{p \in Prime} \sum_{i=1}^{N} \sum_{j=1}^{M} [\gcd(i,j) == p]
∑p∈Prime∑i=1N∑j=1M[gcd(i,j)==p]
套路:
∑
p
∈
P
r
i
m
e
∑
i
=
1
⌊
N
p
⌋
∑
j
=
1
⌊
M
p
⌋
[
gcd
(
i
,
j
)
=
=
1
]
\sum_{p \in Prime} \sum_{i=1}^{\lfloor \frac{N}{p} \rfloor} \sum_{j=1}^{\lfloor \frac{M}{p} \rfloor} [\gcd(i,j) == 1]
∑p∈Prime∑i=1⌊pN⌋∑j=1⌊pM⌋[gcd(i,j)==1]
∑ p ∈ P r i m e ∑ i = 1 ⌊ N p ⌋ ∑ j = 1 ⌊ M p ⌋ [ gcd ( i , j ) = = 1 ] \sum_{p \in Prime} \sum_{i=1}^{\lfloor \frac{N}{p} \rfloor} \sum_{j=1}^{\lfloor \frac{M}{p} \rfloor} [\gcd(i,j) == 1] ∑p∈Prime∑i=1⌊pN⌋∑j=1⌊pM⌋[gcd(i,j)==1]
∑ p ∈ P r i m e ∑ i = 1 ⌊ N p ⌋ ∑ j = 1 ⌊ M p ⌋ ∑ d ∣ i , d ∣ j μ ( d ) \sum_{p \in Prime} \sum_{i=1}^{\lfloor \frac{N}{p} \rfloor} \sum_{j=1}^{\lfloor \frac{M}{p} \rfloor} \sum_{d|i,d|j} \mu(d) ∑p∈Prime∑i=1⌊pN⌋∑j=1⌊pM⌋∑d∣i,d∣jμ(d)
∑ p ∈ P r i m e ∑ d = 1 min ( N , M ) p μ ( d ) × ⌊ N p d ⌋ ⌊ M p d ⌋ \sum_{p \in Prime} \sum_{d=1}^{\frac{\min(N,M)}{p}} \mu(d) \times \lfloor \frac{N}{pd} \rfloor \lfloor \frac{M}{pd} \rfloor ∑p∈Prime∑d=1pmin(N,M)μ(d)×⌊pdN⌋⌊pdM⌋
设 t = p d t=pd t=pd
∑ p ∈ P r i m e ∑ d = 1 min ( N , M ) p μ ( d ) × ⌊ N t ⌋ ⌊ M t ⌋ \sum_{p \in Prime} \sum_{d=1}^{\frac{\min(N,M)}{p}} \mu(d) \times \lfloor \frac{N}{t} \rfloor \lfloor \frac{M}{t} \rfloor ∑p∈Prime∑d=1pmin(N,M)μ(d)×⌊tN⌋⌊tM⌋
∑ t = 1 min ( N , M ) ⌊ N t ⌋ ⌊ M t ⌋ ∑ d ∈ P r i m e , d ∣ t μ ( t d ) \sum_{t=1}^{\min(N,M)} \lfloor \frac{N}{t} \rfloor \lfloor \frac{M}{t} \rfloor \sum_{d \in Prime, d | t} \mu(\frac{t}{d}) ∑t=1min(N,M)⌊tN⌋⌊tM⌋∑d∈Prime,d∣tμ(dt)
套路一下,设 F ( x ) = ∑ d ∈ P r i m e , d ∣ x μ ( x d ) F(x)=\sum_{d \in Prime, d | x} \mu(\frac{x}{d}) F(x)=∑d∈Prime,d∣xμ(dx)
接下来前面部分数论分块就好了。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10;
int mu[N], prime[N], tot = 0, f[N];
bool st[N];
void init(int n) {
mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!st[i]) prime[++tot] = i, mu[i] = -1;
for (int j = 1; j <= tot && prime[j] * i <= n; j++) {
st[prime[j] * i] = 1;
if (i % prime[j] != 0) mu[i * prime[j]] = -mu[i];
else { mu[prime[j] * i] = 0; break; }
}
}
for (int i = 1; i <= tot; i++)
for (int j = prime[i]; j <= n; j += prime[i]) f[j] += mu[j / prime[i]];
for (int i = 1; i <= n; i++) f[i] += f[i - 1];
}
int main() {
init(1e7);
int T, n, m, nn;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m); nn = min(n, m);
long long ans = 0, l = 0, r = 0;
for (int i = 1; i <= nn;) {
l = i; long long x = n / l, y = m / l;
r = min(n / x, m / y);
ans += 1ll * x * 1ll * y * 1ll * (f[r] - f[l - 1]);
i = r + 1;
}
cout << ans << endl;
}
return 0;
}
T7 P1829 【国家集训队】Crash的数字表格 / JZPTAB
题目描述
今天的数学课上,Crash 小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数 a a a 和 b b b, lcm ( a , b ) \text{lcm}(a,b) lcm(a,b) 表示能同时整除 a a a 和 b b b 的最小正整数。例如, lcm ( 6 , 8 ) = 24 \text{lcm}(6, 8) = 24 lcm(6,8)=24。
回到家后,Crash 还在想着课上学的东西,为了研究最小公倍数,他画了一张 $ n \times m$ 的表格。每个格子里写了一个数字,其中第 i i i 行第 j j j 列的那个格子里写着数为 lcm ( i , j ) \text{lcm}(i, j) lcm(i,j)。
看着这个表格,Crash 想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当 n n n 和 m m m 很大时,Crash 就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash 只想知道表格里所有数的和 m o d 20101009 \bmod 20101009 mod20101009 的值。
题目简化:求 ∑ i = 1 n ∑ j = 1 m l c m ( i , j ) \sum_{i=1}^{n} \sum_{j=1}^{m} lcm(i,j) ∑i=1n∑j=1mlcm(i,j)
即 ∑ i = 1 n ∑ j = 1 m i × j gcd ( i , j ) \sum_{i=1}^{n} \sum_{j=1}^{m} \frac{i \times j}{\gcd(i,j)} ∑i=1n∑j=1mgcd(i,j)i×j
∑ d = 1 min ( n , m ) ∑ i = 1 n ∑ j = 1 m i × j d [ gcd ( i , j ) = = d ] \sum_{d=1}^{\min(n,m)} \sum_{i=1}^{n} \sum_{j=1}^{m} \frac{i \times j}{d} [\gcd(i,j) == d] ∑d=1min(n,m)∑i=1n∑j=1mdi×j[gcd(i,j)==d]
∑ d = 1 min ( n , m ) d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i × j × [ gcd ( i , j ) = = 1 ] \sum_{d=1}^{\min(n,m)} d \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{d} \rfloor} i \times j \times [\gcd(i,j) == 1] ∑d=1min(n,m)d∑i=1⌊dn⌋∑j=1⌊dm⌋i×j×[gcd(i,j)==1]
考虑后面部分:
s ( n , m ) = ∑ i = 1 n ∑ j = 1 m i × j × [ gcd ( i , j ) = = 1 ] s(n,m)=\sum_{i=1}^{n} \sum_{j=1}^{m} i \times j \times [\gcd(i,j) == 1] s(n,m)=∑i=1n∑j=1mi×j×[gcd(i,j)==1]
s ( n , m ) = ∑ d = 1 min ( n , m ) μ ( d ) × ∑ d ∣ i n ∑ d ∣ j m i × j s(n,m)=\sum_{d=1}^{\min(n,m)} \mu(d) \times \sum_{d|i}^{n} \sum_{d|j}^{m} i \times j s(n,m)=∑d=1min(n,m)μ(d)×∑d∣in∑d∣jmi×j
s ( n , m ) = ∑ d = 1 min ( n , m ) μ ( d ) × d 2 × ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i × j s(n,m)=\sum_{d=1}^{\min(n,m)} \mu(d) \times d^2 \times \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{d} \rfloor} i \times j s(n,m)=∑d=1min(n,m)μ(d)×d2×∑i=1⌊dn⌋∑j=1⌊dm⌋i×j
这个柿子前半部分可以直接前缀和,后半部分可以公式计算:
设 s u m ( n , m ) = ∑ i = 1 n ∑ j = 1 m i × j = n × ( n + 1 ) 2 × m × ( m + 1 ) 2 sum(n,m)=\sum_{i=1}^{n} \sum_{j=1}^{m} i \times j = \frac{n \times (n + 1)}{2} \times \frac{m \times (m + 1)}{2} sum(n,m)=∑i=1n∑j=1mi×j=2n×(n+1)×2m×(m+1)
则代入原来的式子:
s ( n , m ) = ∑ d = 1 min ( n , m ) μ ( d ) × d 2 × s u m ( ⌊ n d ⌋ , ⌊ m d ⌋ ) s(n,m)=\sum_{d=1}^{\min(n,m)} \mu(d) \times d^2 \times sum(\lfloor \frac{n}{d} \rfloor , \lfloor \frac{m}{d} \rfloor) s(n,m)=∑d=1min(n,m)μ(d)×d2×sum(⌊dn⌋,⌊dm⌋)
至此, s s s 函数可以用数论分块解决了。
带回原式:
A n s = ∑ d = 1 n d × s ( ⌊ n d ⌋ , ⌊ m d ⌋ ) Ans = \sum_{d=1}^{n} d \times s(\lfloor \frac{n}{d} \rfloor, \lfloor \frac{m}{d} \rfloor) Ans=∑d=1nd×s(⌊dn⌋,⌊dm⌋)
这个式子也可以数论分块!
因此这道题双重数论分块即可解决。
#include <bits/stdc++.h>
using namespace std;
const long long N = 1e7 + 10, mod = 20101009;
long long mu[N], prime[N], sum[N], tot;
bool st[N];
inline void init(long long n) {
mu[1] = 1;
for (register long long i = 2; i <= n; i++) {
if (!st[i]) prime[++tot] = i, mu[i] = -1;
for (register long long j = 1; j <= tot && prime[j] * i <= n; j++) {
st[prime[j] * i] = 1;
if (i % prime[j] != 0) mu[i * prime[j]] = -mu[i];
else { mu[prime[j] * i] = 0; break; }
}
}
for (long long i = 1; i <= n; i++) sum[i] = (sum[i - 1] + mu[i] * 1ll * i * i % mod + mod) % mod;
}
long long n, m;
long long Sum(long long x, long long y) {
return (x * (x + 1) / 2 % mod) * (y * (y + 1) / 2 % mod) % mod;
}
inline long long query(long long x, long long y) {
long long ans = 0;
for (long long l = 1, r; l <= min(x, y); l = r + 1) {
r = min(x / (x / l), y / (y / l));
ans = ans + ((sum[r] - sum[l - 1] + mod) * 1ll * Sum(x / l, y / l) % mod) % mod;
}
return ans;
}
int main() {
scanf("%lld%lld", &n, &m);
init(1e7);
long long ans = 0;
for (long long l = 1, r; l <= min(n, m); l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans = (ans + ((r - l + 1) * (l + r) / 2 % mod * (query(n / l, m / l)) % mod)) % mod;
}
printf("%lld\n", ans);
return 0;
}
T8 P3327 【SDOI2015】约数个数和
题目描述
设
d
(
x
)
d(x)
d(x) 为
x
x
x 的约数个数,给定
n
,
m
n,m
n,m,求
∑
i
=
1
n
∑
j
=
1
m
d
(
i
j
)
\sum_{i=1}^n\sum_{j=1}^md(ij)
i=1∑nj=1∑md(ij)
已知一个公式:
d
(
i
j
)
=
∑
x
∣
i
∑
y
∣
j
[
gcd
(
x
,
y
)
=
=
1
]
d(ij) = \sum_{x|i} \sum_{y|j} [\gcd(x,y) == 1]
d(ij)=∑x∣i∑y∣j[gcd(x,y)==1]
则:
A
n
s
=
∑
i
=
1
n
∑
j
=
1
m
∑
x
∣
i
∑
y
∣
j
[
gcd
(
x
,
y
)
=
=
1
]
Ans = \sum_{i=1}^{n} \sum_{j=1}^{m} \sum_{x|i} \sum_{y|j} [\gcd(x,y) == 1]
Ans=∑i=1n∑j=1m∑x∣i∑y∣j[gcd(x,y)==1]
转化式子: ∑ i = 1 n ∑ j = 1 m ⌊ n i ⌋ ⌊ m j ⌋ [ gcd ( x , y ) = = 1 ] \sum_{i=1}^{n} \sum_{j=1}^{m} \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{j} \rfloor [\gcd(x,y) == 1] ∑i=1n∑j=1m⌊in⌋⌊jm⌋[gcd(x,y)==1]
开始莫反: f ( x ) = ∑ i = 1 n ∑ j = 1 m ⌊ n i ⌋ ⌊ m j ⌋ [ gcd ( x , y ) = = x ] f(x)=\sum_{i=1}^{n} \sum_{j=1}^{m} \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{j} \rfloor [\gcd(x,y) == x] f(x)=∑i=1n∑j=1m⌊in⌋⌊jm⌋[gcd(x,y)==x]
g ( x ) = ∑ x ∣ d f ( d ) g(x) = \sum_{x|d} f(d) g(x)=∑x∣df(d)
g ( x ) = ∑ i = 1 n x ∑ j = 1 m x ⌊ n i x ⌋ ⌊ m j x ⌋ g(x) = \sum_{i=1}^{\frac{n}{x}} \sum_{j=1}^{\frac{m}{x}} \lfloor \frac{n}{ix} \rfloor \lfloor \frac{m}{jx} \rfloor g(x)=∑i=1xn∑j=1xm⌊ixn⌋⌊jxm⌋
同时表示出
f
f
f 函数:
f
(
n
)
=
∑
n
∣
d
μ
(
d
n
)
g
(
d
)
f(n) = \sum_{n|d} \mu(\frac{d}{n}) g(d)
f(n)=∑n∣dμ(nd)g(d)
答案明显是
f
(
1
)
f(1)
f(1):
A
n
s
=
f
(
1
)
=
∑
i
=
1
n
μ
(
i
)
g
(
i
)
Ans = f(1) = \sum_{i=1}^{n} \mu(i) g(i)
Ans=f(1)=∑i=1nμ(i)g(i)
最后是计算 g ( i ) g(i) g(i) 的问题:先计算 s ( n ) = ∑ i = 1 n ⌊ n i ⌋ s(n) = \sum_{i=1}^{n} \lfloor \frac{n}{i} \rfloor s(n)=∑i=1n⌊in⌋
这样 A n s Ans Ans 即可分为前后两部分( μ ( i ) \mu(i) μ(i) 和 g ( i ) g(i) g(i))计算。
前面 μ \mu μ 直接线性筛并前缀和,后面 g g g 函数用 s s s 直接求解。
#include <bits/stdc++.h>
using namespace std;
const long long N = 5e4 + 10;
long long s[N], mu[N], tot = 0, prime[N], sum[N];
bool st[N];
inline void init(long long n) {
sum[1] = mu[1] = 1;
for (register long long i = 2; i <= n; i++) {
if (!st[i]) prime[++tot] = i, mu[i] = -1;
for (register long long j = 1; j <= tot && prime[j] * i <= n; j++) {
st[prime[j] * i] = 1;
if (i % prime[j] != 0) mu[i * prime[j]] = -mu[i];
else { mu[prime[j] * i] = 0; break; }
}
sum[i] = sum[i - 1] + mu[i];
}
for (long long i = 1; i <= n; i++) {
for (long long l = 1, r; l <= i; l = r + 1) {
r = i / (i / l);
s[i] += (r - l + 1) * (i / l);
}
}
}
long long n, m;
int main() {
long long T; scanf("%lld", &T);
init(5e4);
while (T--) {
scanf("%lld%lld", &n, &m);
if (n > m) swap(n, m);
long long ans = 0;
for (long long l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += (sum[r] - sum[l - 1]) * s[n / l] * s[m / l];
}
printf("%lld\n", ans);
}
return 0;
}
T9 P4449 于神之怒加强版
题目描述
给定 n , m , k n,m,k n,m,k,计算
∑ i = 1 n ∑ j = 1 m gcd ( i , j ) k \sum_{i=1}^n \sum_{j=1}^m \gcd(i,j)^k i=1∑nj=1∑mgcd(i,j)k
对 1 0 9 + 7 10^9 + 7 109+7 取模的结果。
∑ i = 1 n ∑ j = 1 m gcd ( i , j ) k \sum_{i=1}^n \sum_{j=1}^m \gcd(i,j)^k ∑i=1n∑j=1mgcd(i,j)k
枚举 gcd \gcd gcd,得:
∑ i = 1 n ∑ j = 1 m ∑ d = 1 n d k [ gcd ( i , j ) = = 1 ] \sum_{i=1}^{n} \sum_{j=1}^{m} \sum_{d=1}^{n} d^{k} [\gcd(i,j) == 1] ∑i=1n∑j=1m∑d=1ndk[gcd(i,j)==1]
∑ d = 1 d k ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ x ∣ i , x ∣ j μ ( x ) \sum_{d=1} d^{k} \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{d} \rfloor} \sum_{x|i,x|j} \mu(x) ∑d=1dk∑i=1⌊dn⌋∑j=1⌊dm⌋∑x∣i,x∣jμ(x)
∑ d = 1 n d k ∑ x = 1 μ ( x ) ⌊ n d x ⌋ ⌊ m d x ⌋ \sum_{d=1}^{n} d^{k} \sum_{x=1} \mu(x) \lfloor \frac{n}{dx} \rfloor \lfloor \frac{m}{dx} \rfloor ∑d=1ndk∑x=1μ(x)⌊dxn⌋⌊dxm⌋
∑ d = 1 n d k ∑ x = 1 μ ( x ) ⌊ n d x ⌋ ⌊ m d x ⌋ \sum_{d=1}^{n} d^k \sum_{x=1} \mu(x) \lfloor \frac{n}{dx} \rfloor \lfloor \frac{m}{dx} \rfloor ∑d=1ndk∑x=1μ(x)⌊dxn⌋⌊dxm⌋
简化式子,设 T = d x T = dx T=dx:
∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ ∑ d ∣ T d k μ ( T d ) \sum_{T=1}^{n} \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \sum_{d|T} d^{k} \mu(\frac{T}{d}) ∑T=1n⌊Tn⌋⌊Tm⌋∑d∣Tdkμ(dT)
插入证明积性函数的卷积仍是积性函数
设 f ( x ) f(x) f(x)、 g ( x ) g(x) g(x) 都是积性函数。
设 a a a 和 b b b,其中 gcd ( a , b ) = = 1 \gcd(a,b) == 1 gcd(a,b)==1。
设 h ( a ) = ∑ x ∣ a f ( a ) g ( a x ) h(a) = \sum_{x|a} f(a) g(\frac{a}{x}) h(a)=∑x∣af(a)g(xa)
h ( b ) = ∑ y ∣ b f ( b ) g ( b y ) h(b) = \sum_{y|b} f(b) g(\frac{b}{y}) h(b)=∑y∣bf(b)g(yb)
接下来证明 h ( a ) h ( b ) = = h ( a b ) h(a)h(b) == h(ab) h(a)h(b)==h(ab)
h ( a ) h ( b ) = ∑ x ∣ a f ( a ) g ( a x ) ∑ y ∣ b f ( b ) g ( b y ) h(a)h(b) = \sum_{x|a} f(a) g(\frac{a}{x}) \sum_{y|b} f(b) g(\frac{b}{y}) h(a)h(b)=∑x∣af(a)g(xa)∑y∣bf(b)g(yb)
= ∑ d ∣ a b f ( d ) g ( a b d ) = \sum_{d|ab} f(d) g(\frac{ab}{d}) =∑d∣abf(d)g(dab)
= h ( a b ) = h(ab) =h(ab)
回到原式: ∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ ∑ d ∣ T d k μ ( T d ) \sum_{T=1}^{n} \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \sum_{d|T} d^{k} \mu(\frac{T}{d}) ∑T=1n⌊Tn⌋⌊Tm⌋∑d∣Tdkμ(dT)
只观察后半部分: ∑ d ∣ T d k μ ( T d ) \sum_{d|T} d^{k} \mu(\frac{T}{d}) ∑d∣Tdkμ(dT)
很明显 d k d^k dk 和 μ \mu μ 都是积性函数,而且这是一个卷积的形式。
因此利用上面插入证明的结论:积性函数的卷积仍是积性函数。
也就是说 g ( T ) = ∑ d ∣ T d k μ ( T d ) g(T) = \sum_{d|T} d^{k} \mu(\frac{T}{d}) g(T)=∑d∣Tdkμ(dT) 也是积性函数。
众所周知积性函数可以用线性筛。因此 g g g 函数也可以用筛法。
下面分类讨论两种情况:
线性筛 g g g 函数即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e6 + 10;
const int mod = 1e9 + 7;
bool st[N];
int prime[N], tot = 0, g[N], ksm[N];
int qmi(int a, int b) {
int res = 1 % mod;
while (b) {
if (b & 1) res = (long long)res * a % mod;
a = (long long)a * a % mod;
b >>= 1;
}
return res;
}
int k;
void init(int n) {
g[1] = 1;
for (int i = 2; i <= n; i++) {
if (!st[i]) prime[++tot] = i, ksm[tot] = qmi(i, k), g[i] = (ksm[tot] - 1) % mod;
for (int j = 1; j <= tot && i * prime[j] <= n; j++) {
int p = prime[j] * i;
st[p] = 1;
if (i % prime[j] == 0) {
g[p] = 1ll * ksm[j] * g[i] % mod;
break;
}
g[p] = 1ll * g[i] * g[prime[j]] % mod;
}
}
for (int i = 2; i <= n; i++) g[i] += g[i - 1], g[i] %= mod;
}
int T, n, m;
int main() {
scanf("%d%d", &T, &k);
init(N - 5);
while (T--) {
scanf("%d%d", &n, &m);
int ans = 0;
for (int l = 1, r; l <= min(n, m); l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += ((g[r] - g[l - 1] + mod) % mod * 1ll * (n / l) % mod * (m / l) % mod) % mod;
ans %= mod;
}
printf("%d\n", ans);
}
return 0;
}
T10 P3312 【SDOI2014】数表
题目描述
有一张 n × m n\times m n×m 的数表,其第 i i i 行第 j j j 列( 1 ≤ i ≤ n 1\le i\le n 1≤i≤n, 1 ≤ j ≤ m 1\le j\le m 1≤j≤m)的数值为能同时整除 i i i 和 j j j 的所有自然数之和。给定 a a a,计算数表中不大于 a a a 的数之和。
简化题意:求 ∑ i = 1 n ∑ j = 1 m σ ( gcd ( i , j ) ) [ σ ( gcd ( i , j ) ) ≤ a ] \sum_{i=1}^{n} \sum_{j=1}^{m} \sigma(\gcd(i,j)) [\sigma(\gcd(i,j)) \leq a] ∑i=1n∑j=1mσ(gcd(i,j))[σ(gcd(i,j))≤a]
其中 σ ( x ) \sigma(x) σ(x) 表示 x x x 的约数和。
目前考虑没有 a a a 的限制的情况。
则 a n s = ∑ d = 1 min ( n , m ) σ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ gcd ( i , j ) = = 1 ] ans = \sum_{d=1}^{\min(n,m)} \sigma(d) \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{d} \rfloor} [\gcd(i,j)==1] ans=∑d=1min(n,m)σ(d)∑i=1⌊dn⌋∑j=1⌊dm⌋[gcd(i,j)==1]
∑ d = 1 min ( n , m ) σ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ k ∣ i , k ∣ j μ ( k ) \sum_{d=1}^{\min(n,m)} \sigma(d) \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{d} \rfloor} \sum_{k|i,k|j} \mu(k) ∑d=1min(n,m)σ(d)∑i=1⌊dn⌋∑j=1⌊dm⌋∑k∣i,k∣jμ(k)
∑ d = 1 min ( n , m ) σ ( d ) ∑ k = 1 ⌊ min ( n , m ) d ⌋ μ ( k ) ⌊ n d k ⌋ ⌊ m d k ⌋ \sum_{d=1}^{\min(n,m)} \sigma(d) \sum_{k=1}^{\lfloor \frac{\min(n,m)}{d} \rfloor} \mu(k) \lfloor \frac{n}{dk} \rfloor \lfloor \frac{m}{dk} \rfloor ∑d=1min(n,m)σ(d)∑k=1⌊dmin(n,m)⌋μ(k)⌊dkn⌋⌊dkm⌋
设 T = d k T=dk T=dk:
∑ T = 1 min ( n , m ) ∑ d ∣ T σ ( d ) μ ( T d ) ⌊ n T ⌋ ⌊ m T ⌋ \sum_{T=1}^{\min(n,m)} \sum_{d|T} \sigma(d) \mu(\frac{T}{d}) \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor ∑T=1min(n,m)∑d∣Tσ(d)μ(dT)⌊Tn⌋⌊Tm⌋
加入 a a a 的限制,考虑用树状数组维护 ∑ d σ ( d ) μ ( T d ) \sum_d \sigma(d) \mu(\frac{T}{d}) ∑dσ(d)μ(dT) 的前缀和。
将线性筛的 σ \sigma σ 升序排序,从小到大查询 a a a 并且更新。
#include <bits/stdc++.h>
using namespace std;
const long long N = 1e5 + 10, M = 2e4 + 10;
const long long mod = (1 << 31);
long long n, prime[N], tot, tree[N], g[N], ans[M], mu[N];
pair<int, int> d[N];
bool st[N];
inline void insert(long long x, long long k) {
for (; x <= N; x += x & -x) tree[x] += k;
}
inline long long query(long long x) {
long long ans = 0;
for (; x; x -= x & -x) ans += tree[x];
return ans;
}
struct ask {
long long n, m, a, id;
long long sol() {
if (n > m) swap(n, m);
long long res = 0;
for (long long l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
res += ((query(r) - query(l - 1)) * (n / l) * (m / l));
}
return res;
}
} q[M];
bool cmp(ask a, ask b) {
return a.a < b.a;
}
void init() {
mu[1] = 1; d[1] = make_pair(1, 1);
for (long long i = 2; i <= N; i++) {
if (!st[i]) prime[++tot] = i, g[i] = i + 1, mu[i] = -1, d[i] = make_pair(i + 1, i);
for (long long j = 1; j <= tot && i * prime[j] <= N; j++) {
long long x = i * prime[j];
st[x] = 1;
if (i % prime[j] == 0) {
mu[x] = 0;
g[x] = g[i] * prime[j] + 1;
d[x] = make_pair(d[i].first / g[i] * g[x], x);
break;
}
mu[x] = -mu[i];
g[x] = prime[j] + 1;
d[x] = make_pair(d[i].first * d[prime[j]].first, x);
}
}
}
int main() {
init();
long long T; scanf("%lld", &T);
sort(d + 1, d + 1 + N);
for (long long i = 1; i <= T; i++)
scanf("%lld%lld%lld", &q[i].n, &q[i].m, &q[i].a), q[i].id = i;
sort(q + 1, q + 1 + T, cmp);
for (long long i = 1, j = 1; i <= T; i++) {
while (d[j].first <= q[i].a && j <= N) {
for (long long k = d[j].second; k <= N; k += d[j].second) insert(k, d[j].first * mu[k / d[j].second]);
j++;
}
ans[q[i].id] = q[i].sol();
}
for (long long i = 1; i <= T; i++) printf("%lld\n", ans[i] % mod);
return 0;
}
T11 P3704 【SDOI2017】数字表格
题目背景
Doris 刚刚学习了 fibonacci 数列。用 f i f_i fi 表示数列的第 i i i 项,那么
f 0 = 0 , f 1 = 1 f_0=0,f_1=1 f0=0,f1=1
f n = f n − 1 + f n − 2 , n ≥ 2 f_n=f_{n-1}+f_{n-2},n\geq 2 fn=fn−1+fn−2,n≥2
题目描述
Doris 用老师的超级计算机生成了一个 n × m n\times m n×m 的表格,
第 i i i 行第 j j j 列的格子中的数是 f gcd ( i , j ) f_{\gcd(i,j)} fgcd(i,j),其中 gcd ( i , j ) \gcd(i,j) gcd(i,j) 表示 i , j i,j i,j 的最大公约数。
Doris 的表格中共有 n × m n\times m n×m 个数,她想知道这些数的乘积是多少。
答案对 1 0 9 + 7 10^9+7 109+7 取模。
简化题意:求 ∏ d = 1 n ∏ i = 1 n ∏ j = 1 m f i b ( gcd ( i , j ) ) [ gcd ( i , j ) = = d ] \prod_{d=1}^{n} \prod_{i=1}^{n} \prod_{j=1}^{m} fib(\gcd(i,j)) [\gcd(i,j) == d] ∏d=1n∏i=1n∏j=1mfib(gcd(i,j))[gcd(i,j)==d]
其中 f i b ( x ) fib(x) fib(x) 是斐波那契数列第 x x x 项。
化简式子,即提出 d d d:
∏ d = 1 n f i b ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ gcd ( i , j ) = = 1 ] \prod_{d=1}^{n} fib(d)^{\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{d} \rfloor} [\gcd(i,j) == 1]} ∏d=1nfib(d)∑i=1⌊dn⌋∑j=1⌊dm⌋[gcd(i,j)==1]
∵ ∑ i = 1 a ∑ j = 1 b [ gcd ( a , b ) = = 1 ] \because \sum_{i=1}^{a} \sum_{j=1}^{b} [\gcd(a,b) == 1] ∵∑i=1a∑j=1b[gcd(a,b)==1] 与 ∑ d = 1 a ⌊ a d ⌋ ⌊ b d ⌋ μ ( d ) \sum_{d=1}^{a} \lfloor \frac{a}{d} \rfloor \lfloor \frac{b}{d} \rfloor \mu(d) ∑d=1a⌊da⌋⌊db⌋μ(d) 相等,
∴ \therefore ∴ 原式 = ∏ d = 1 n f i b ( d ) ∑ k ⌊ n d k ⌋ ⌊ m d k ⌋ μ ( k ) = \prod_{d=1}^{n} fib(d) ^{\sum_{k} \lfloor \frac{n}{dk} \rfloor \lfloor \frac{m}{dk} \rfloor \mu(k)} =∏d=1nfib(d)∑k⌊dkn⌋⌊dkm⌋μ(k)
设 T = d k T=dk T=dk:
∏ T = 1 n ∏ k ∣ T f i b ( T k ) μ ( k ) ⌊ n T ⌋ ⌊ m T ⌋ \prod_{T=1}^{n} \prod_{k|T} fib(\frac{T}{k}) ^{\mu(k) \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor} ∏T=1n∏k∣Tfib(kT)μ(k)⌊Tn⌋⌊Tm⌋
= ∏ T = 1 n ∏ k ∣ T ( f i b ( T k ) μ ( k ) ) ⌊ n T ⌋ ⌊ m T ⌋ =\prod_{T=1}^{n} \prod_{k|T} (fib(\frac{T}{k}) ^{\mu(k)})^{\lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor} =∏T=1n∏k∣T(fib(kT)μ(k))⌊Tn⌋⌊Tm⌋
对于中间的 ( f i b ( T k ) μ ( k ) ) (fib(\frac{T}{k}) ^{\mu(k)}) (fib(kT)μ(k)),可以直接 n log n n~\log~n n log n 预处理前缀和。
对于旁边的指数 ⌊ n T ⌋ ⌊ m T ⌋ ^{\lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor} ⌊Tn⌋⌊Tm⌋ 可以直接数论分块。
注意当需要以 μ \mu μ 为指数的时候,因为 μ \mu μ 可能是 − 1 -1 −1,所以需要预先求出 f i b fib fib 数组的逆元。
f i b fib fib 数组直接暴力即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10, mod = 1e9 + 7;
int mu[N], prime[N], tot = 0, sum[N], fib[N], inv_fib[N];
bool st[N];
int read(){
int x = 0; int zf = 1; char ch = ' ';
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
int qmi(int a, int b, int p) {
int res = 1;
while (b) {
if (b & 1) res = res * 1ll * a % p;
b >>= 1;
a = a * 1ll * a % p;
}
return res;
}
int ny(int a) { return qmi(a, mod - 2, mod); }
inline void init(int n) {
fib[0] = 0, fib[1] = 1;
for (int i = 2; i <= n; i++) fib[i] = (fib[i - 1] + fib[i - 2]) % mod, inv_fib[i] = ny(fib[i]);
mu[1] = 1; inv_fib[1] = 1;
for (register int i = 2; i <= n; i++) {
if (!st[i]) prime[++tot] = i, mu[i] = -1;
for (register int j = 1; j <= tot && prime[j] * i <= n; j++) {
st[prime[j] * i] = 1;
if (i % prime[j] != 0) mu[i * prime[j]] = -mu[i];
else { mu[prime[j] * i] = 0; break; }
}
}
for (int i = 0; i <= n; i++) sum[i] = 1;
for (int i = 1; i <= n; i++) {
if (mu[i] == 0) continue;
for (int j = i; j <= n; j += i) {
sum[j] = (long long) sum[j] * (mu[i] == 1 ? fib[j / i] : inv_fib[j / i]) % mod;
}
}
for (int i = 1; i <= n; i++) sum[i] = (long long) sum[i - 1] * sum[i] % mod;
}
int solve(int n, int m) {
if (n > m) swap(n, m);
int ans = 1;
for (int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans = (long long) ans * qmi(1ll * sum[r] * ny(sum[l - 1]) % mod, 1ll * (n / l) * (m / l) % (mod - 1), mod) % mod;
}
return ans;
}
int n, m;
int main() {
init(N - 10);
int T; scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
printf("%d\n", solve(n, m));
}
return 0;
}
Markdown代码超过 1000 行了
T12 CF1780F Three Chairs
题面翻译
给定一个数组
a
1
,
a
2
,
⋯
,
a
n
a_1,a_2,\cdots,a_n
a1,a2,⋯,an,求满足以下条件的三元组
(
a
k
1
,
a
k
2
,
a
k
3
)
(a_{k_1},a_{k_2},a_{k_3})
(ak1,ak2,ak3)个数:
gcd
(
max
(
a
k
1
,
a
k
2
,
a
k
3
)
,
min
(
a
k
1
,
a
k
2
,
a
k
3
)
)
=
=
1
\gcd(\max(a_{k_1},a_{k_2},a_{k_3}), \min(a_{k_1},a_{k_2},a_{k_3}))==1
gcd(max(ak1,ak2,ak3),min(ak1,ak2,ak3))==1
首先对于 gcd ( max ( a k 1 , a k 2 , a k 3 ) , min ( a k 1 , a k 2 , a k 3 ) ) = = 1 \gcd(\max(a_{k_1},a_{k_2},a_{k_3}), \min(a_{k_1},a_{k_2},a_{k_3}))==1 gcd(max(ak1,ak2,ak3),min(ak1,ak2,ak3))==1,假设 a k 1 ≤ a k 2 ≤ a k 3 a_{k_1} \le a_{k_2} \le a_{k_3} ak1≤ak2≤ak3,则原式变为: gcd ( a k 1 , a k 3 ) = = 1 \gcd(a_{k_1},a_{k_3}) == 1 gcd(ak1,ak3)==1。
发现它与 a k 2 a_{k_2} ak2 没有任何关系。因此只需要枚举最大值和最小值。
那么根据原来的假设,我们先把 a a a 数组排序,接下来的工作就减轻很多。
答案即对于每个 d ∣ a i d|a_i d∣ai,把 i i i 加入序列 q q q。选择最大最小值以后再挑选中间的数。因此就是挑选 q i > q j q_i > q_j qi>qj 的所有数对的 q i − q j − 1 q_i - q_j - 1 qi−qj−1 之和。
开始莫反:
设 f ( d ) f(d) f(d) 表示满足以下条件的三元组 ( a k 1 , a k 2 , a k 3 ) (a_{k_1},a_{k_2},a_{k_3}) (ak1,ak2,ak3) 的个数 gcd ( max ( a k 1 , a k 2 , a k 3 ) , min ( a k 1 , a k 2 , a k 3 ) ) = = d \gcd(\max(a_{k_1},a_{k_2},a_{k_3}), \min(a_{k_1},a_{k_2},a_{k_3}))==d gcd(max(ak1,ak2,ak3),min(ak1,ak2,ak3))==d
g ( d ) g(d) g(d) 表示满足以下条件的三元组 ( a k 1 , a k 2 , a k 3 ) (a_{k_1},a_{k_2},a_{k_3}) (ak1,ak2,ak3) 的个数 d ∣ gcd ( max ( a k 1 , a k 2 , a k 3 ) , min ( a k 1 , a k 2 , a k 3 ) ) d| \gcd(\max(a_{k_1},a_{k_2},a_{k_3}), \min(a_{k_1},a_{k_2},a_{k_3})) d∣gcd(max(ak1,ak2,ak3),min(ak1,ak2,ak3))
g ( x ) = ∑ x ∣ d f ( d ) g(x) = \sum_{x|d} f(d) g(x)=∑x∣df(d)
即 f ( x ) = ∑ x ∣ d μ ( d x ) g ( d ) f(x) = \sum_{x|d} \mu(\frac{d}{x}) g(d) f(x)=∑x∣dμ(xd)g(d)
答案显然为 f ( 1 ) f(1) f(1),即 ∑ i = 1 n μ ( i ) g ( i ) \sum_{i=1}^{n} \mu(i) g(i) ∑i=1nμ(i)g(i)
接下来直接按照上述方式解即可。
#include <bits/stdc++.h>
using namespace std;
const long long N = 3e5 + 10;
long long mu[N], prime[N] , tot = 0;
bool st[N];
long long n, a[N];
inline void init(long long n) {
mu[1] = 1;
for (register long long i = 2; i <= n; i++) {
if (!st[i]) prime[++tot] = i, mu[i] = -1;
for (register long long j = 1; j <= tot && prime[j] * i <= n; j++) {
st[prime[j] * i] = 1;
if (i % prime[j] != 0) mu[i * prime[j]] = -mu[i];
else { mu[prime[j] * i] = 0; break; }
}
}
}
long long sum[N], cnt[N];
long long ans;
void work(long long x, long long y) {
if (mu[x] == 0) return;
ans += 1ll * mu[x] * (1ll * (y - 1) * 1ll * cnt[x] - sum[x]);
sum[x] += y; cnt[x]++;
}
int main() {
init(N - 10);
scanf("%d", &n);
for (long long i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
for (long long i = 1; i <= n; i++) {
for (long long j = 1; j * j <= a[i]; j++) {
if (a[i] % j != 0) continue;
work(j, i);
if (j * j != a[i]) work(a[i] / j, i);
}
}
printf("%lld\n", ans);
return 0;
}
T13 CF235E Number Challenge
题面翻译
题意:记d(i)表示i的约数个数,输入a,b,c;
求
∑
i
=
1
a
∑
j
=
1
b
∑
k
=
1
c
d
(
i
j
k
)
m
o
d
(
1073741824
(
2
30
)
)
\sum_{i=1}^{a} \sum_{j=1}^{b} \sum_{k=1}^{c} d(ijk) \mod(1073741824(2^{30}))
∑i=1a∑j=1b∑k=1cd(ijk)mod(1073741824(230)) 的值。
其中 d ( i ) d(i) d(i) 表示 i i i 的约数个数。
回顾 T8 P3327 【SDOI2015】约数个数和
其中给出了一个公式。这里拓展到三个数的情况:
将原公式修改为: ∑ i = 1 A ∑ j = 1 B ∑ k = 1 C ∑ a ∣ i ∑ b ∣ j ∑ c ∣ k [ gcd ( a , b , c ) = = 1 ] \sum_{i=1}^{A} \sum_{j=1}^{B} \sum_{k=1}^{C} \sum_{a|i} \sum_{b|j} \sum_{c|k} [\gcd(a,b,c) == 1] ∑i=1A∑j=1B∑k=1C∑a∣i∑b∣j∑c∣k[gcd(a,b,c)==1]
∑ a = 1 A ∑ b = 1 B ∑ c = 1 C ∑ a ∣ i ∑ b ∣ j ∑ c ∣ k [ gcd ( a , b , c ) = = 1 ] \sum_{a=1}^{A} \sum_{b=1}^{B} \sum_{c=1}^{C} \sum_{a|i} \sum_{b|j} \sum_{c|k} [\gcd(a,b,c) == 1] ∑a=1A∑b=1B∑c=1C∑a∣i∑b∣j∑c∣k[gcd(a,b,c)==1]
∑ a = 1 A ∑ b = 1 B ∑ c = 1 C ⌊ A a ⌋ ⌊ B b ⌋ ⌊ C c ⌋ [ gcd ( a , b , c ) = = 1 ] \sum_{a=1}^{A} \sum_{b=1}^{B} \sum_{c=1}^{C} \lfloor \frac{A}{a} \rfloor \lfloor \frac{B}{b} \rfloor \lfloor \frac{C}{c} \rfloor [\gcd(a,b,c) == 1] ∑a=1A∑b=1B∑c=1C⌊aA⌋⌊bB⌋⌊cC⌋[gcd(a,b,c)==1]
∑ i = 1 A ∑ j = 1 B ∑ k = 1 C ⌊ A i ⌋ ⌊ B j ⌋ ⌊ C k ⌋ [ gcd ( i , j , k ) = = 1 ] \sum_{i=1}^{A} \sum_{j=1}^{B} \sum_{k=1}^{C} \lfloor \frac{A}{i} \rfloor \lfloor \frac{B}{j} \rfloor \lfloor \frac{C}{k} \rfloor [\gcd(i,j,k) == 1] ∑i=1A∑j=1B∑k=1C⌊iA⌋⌊jB⌋⌊kC⌋[gcd(i,j,k)==1]
∑ i = 1 A ∑ j = 1 B ∑ k = 1 C ⌊ A i ⌋ ⌊ B j ⌋ ⌊ C k ⌋ ∑ d ∣ gcd ( i , j ) μ ( d ) [ gcd ( i , k ) = = 1 ] [ gcd ( j , k ) = = 1 ] \sum_{i=1}^{A} \sum_{j=1}^{B} \sum_{k=1}^{C} \lfloor \frac{A}{i} \rfloor \lfloor \frac{B}{j} \rfloor \lfloor \frac{C}{k} \rfloor \sum_{d|\gcd(i,j)} \mu(d) [\gcd(i,k) == 1][\gcd(j,k) == 1] ∑i=1A∑j=1B∑k=1C⌊iA⌋⌊jB⌋⌊kC⌋∑d∣gcd(i,j)μ(d)[gcd(i,k)==1][gcd(j,k)==1]
∑ k = 1 C ∑ d = 1 min ( A , B ) μ ( d ) ∑ i = 1 ⌊ A d ⌋ ⌊ A i d ⌋ [ gcd ( i d , k ) = = 1 ] ∑ j = 1 ⌊ B d ⌋ ⌊ B i d ⌋ [ gcd ( j d , k ) = = 1 ] \sum_{k=1}^{C} \sum_{d=1}^{\min(A,B)} \mu(d) \sum_{i=1}^{\lfloor \frac{A}{d} \rfloor} \lfloor \frac{A}{id} \rfloor [\gcd(id,k) == 1] \sum_{j=1}^{\lfloor \frac{B}{d} \rfloor} \lfloor \frac{B}{id} \rfloor [\gcd(jd,k) == 1] ∑k=1C∑d=1min(A,B)μ(d)∑i=1⌊dA⌋⌊idA⌋[gcd(id,k)==1]∑j=1⌊dB⌋⌊idB⌋[gcd(jd,k)==1]
这个公式就是此题答案了。
在上代码之前有两个优化:
- 预处理 ∑ i = 1 ⌊ A d ⌋ ⌊ A i d ⌋ [ gcd ( i d , k ) = = 1 ] \sum_{i=1}^{\lfloor \frac{A}{d} \rfloor} \lfloor \frac{A}{id} \rfloor [\gcd(id,k) == 1] ∑i=1⌊dA⌋⌊idA⌋[gcd(id,k)==1] 和 ∑ j = 1 ⌊ B d ⌋ ⌊ B i d ⌋ [ gcd ( j d , k ) = = 1 ] \sum_{j=1}^{\lfloor \frac{B}{d} \rfloor} \lfloor \frac{B}{id} \rfloor [\gcd(jd,k) == 1] ∑j=1⌊dB⌋⌊idB⌋[gcd(jd,k)==1]
- 用数组记录两个数是否互质。(即后面的判定条件 [ gcd ( . . . , . . . ) = = 1 ] [\gcd(...~,~...) == 1] [gcd(... , ...)==1]
代码:
#include <bits/stdc++.h>
using namespace std;
const long long N = 2e3 + 10;
const long long mod = 1073741824;
long long n, m;
long long tot, prime[N], mu[N], sum[N];
long long a, b, c;
bool st[N];
long long gcd(int a, int b) // 欧几里得算法
{
return b ? gcd(b, a % b) : a;
}
inline void init1(long long n) {
sum[1] = mu[1] = 1;
for (long long i = 2; i <= n; i++) {
if (!st[i]) prime[++tot] = i, mu[i] = -1;
for (long long j = 1; j <= tot && prime[j] * i <= n; j++) {
st[prime[j] * i] = 1;
if (i % prime[j] != 0) mu[i * prime[j]] = -mu[i];
else { mu[prime[j] * i] = 0; break; }
}
sum[i] = sum[i - 1] + mu[i];
}
}
long long A[N][N], B[N][N], have[N][N];
void init2() {
for (long long i = 1; i <= 2000; i++)
for (long long j = 1; j <= 2000; j++)
if (gcd(i, j) == 1) have[i][j] = true; //互质
for (long long k = 1; k <= c; k++)
for (long long d = 1; d <= min(a, b); d++) {
for (long long i = 1; i <= a / d; i++)
if (have[k][i * d]) //互质
A[k][d] = (A[k][d] + a / (i * d)) % mod;
for (long long i = 1; i <= b / d; i++)
if (have[k][i * d]) //互质
B[k][d] = (B[k][d] + b / (i * d)) % mod;
}
}
int main() {
scanf("%d%d%d", &a, &b, &c);
init1(2000), init2(); //预处理
long long ans = 0;
for (long long k = 1; k <= c; k++) { //公式中最外层循环
long long res = 0;
for (long long d = 1; d <= min(a, b); d++)
res = (res + mu[d] % mod * A[k][d] * B[k][d] % mod) % mod;
ans = (ans + res * (c / k) % mod) % mod;
}
printf("%lld\n", (ans % mod + mod) % mod);
return 0;
}
到这里这 13 13 13 题就结束了
公式有点多,我就写了大概两三周的样子。。。
第十三题是我生病的时候写的