Divide Both题解
1.前言
我是 sb ,思路想到了,但时间复杂度算错了。。。
2.题解
正难则反,我们可以将答案拆成:总方案数 - gcd = 1(即互质)的情况 - gcd (a, b) = Min (a, b) 的情况
gcd (a, b) = Min (a, b) 的情况比较好求,直接埃筛就行了(1的情况比较特殊,需要特判,细节见代码)。
互质的情况怎么处理呢,我们暴力枚举 i ∈ [ l , r ] i \in [l, r] i∈[l,r],求出 [ l , r ] [l, r] [l,r] 中与 i i i 互质的数的个数再求和就行了。
现在我们来思考怎么求 [ l , r ] [l, r] [l,r] 中与 i i i 互质的数的个数。
s o l v e ( x , y ) solve(x, y) solve(x,y) 表示在 [ 1 , y ] [1, y] [1,y] 中与 x x x 互质的数的个数,则单个答案为 s o l v e ( i , r ) − s o l v e ( i , l − 1 ) solve (i, r) - solve (i, l - 1) solve(i,r)−solve(i,l−1)。
s o l v e solve solve 的求法不难想到容斥原理。
令 x = p 1 q 1 p 2 q 2 . . . p n q n x = p_1^{q_1} p_2^{q_2} ... p_n^{q_n} x=p1q1p2q2...pnqn
则 [ 1 , y ] [1, y] [1,y] 中与 x x x 互质的数字共有:
y − ∑ i = 1 n ⌊ y p i ⌋ + ∑ i = 1 n ∑ j = i + 1 n ⌊ y p i p j ⌋ − . . . . y-\sum_{i=1}^{n}\lfloor\frac{y}{p_i}\rfloor+\sum_{i=1}^n\sum_{j=i+1}^n\lfloor\frac{y}{p_ip_j}\rfloor-.... y−∑i=1n⌊piy⌋+∑i=1n∑j=i+1n⌊pipjy⌋−....
注意优化:
不能单独每个
x
x
x 去分解质因数,时间复杂度会变为
O
(
n
n
)
O (n \sqrt {n})
O(nn),所以我们可以用埃筛,用每个质数去筛,每个筛到的数在
v
e
c
t
o
r
vector
vector 中记录下来这个质因数。
3.参考代码
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
#define LL long long
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T>
void write (T x) {
if (x < 0) {
x = -x;
putchar ('-');
}
if (x < 10) {
putchar (x + '0');
return;
}
write (x / 10);
putchar (x % 10 + '0');
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
const int Maxn = 1e6;
int l, r;
int cnt, primes[Maxn + 5];
bool vis[Maxn + 5];
vector <int> v[Maxn + 5];
void Euler () {
for (int i = 2; i <= Maxn; i++) {
if (vis[i] == 0)
primes[++cnt] = i;
for (int j = i * 2; j <= Maxn; j += i)
vis[j] = 1;
}
//求质数
for (int i = 1; i <= cnt; i++) {
for (int j = primes[i]; j <= Maxn; j += primes[i]) {
v[j].push_back (primes[i]);
//primes[i]是j的一个质因数
}
}
}
int mul, res;
//mul记录下容斥时的分母
void dfs (int step, int Need, int Up, int x, int y) {
//在[step, v[x].size () - 1]中选择 Need 个,总共需要选择 Up 个数
if (Need == 0) {
res += ((Up & 1) == 1 ? -1 : 1) * (y / mul);
return;
}
if (step == (int)v[x].size ()) return;
dfs (step + 1, Need, Up, x, y);
mul *= v[x][step];
dfs (step + 1, Need - 1, Up, x, y);
mul /= v[x][step];
}
int solve (int x, int y) {
res = y;
for (int i = 1; i <= (int)v[x].size (); i++) {
mul = 1;
dfs (0, i, i, x, y);
}
return res;
}
signed main () {
Euler ();
read (l); read (r);
int ans = 0;
for (int i = l; i <= r; i++)
for (int j = i * 2; j <= r; j += i)
ans += 2;
//gcd (a, b) = Min (a, b) 的情况
if (l != 1)
for (int i = l; i <= r; i++)
ans += solve (i, r) - solve (i, l - 1);
else {//等于1需要特判
ans -= r - 1;//(i, 1)数对的情况会多统计一次,所以需要减掉
for (int i = 2; i <= r; i++)
ans += solve (i, r) - solve (i, l - 1);
}
write ((r - l + 1) * (r - l) - ans);
return 0;
}