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] 中与 iii 互质的数的个数再求和就行了。
现在我们来思考怎么求 [l,r][l, r][l,r] 中与 iii 互质的数的个数。
solve(x,y)solve(x, y)solve(x,y) 表示在 [1,y][1, y][1,y] 中与 xxx 互质的数的个数,则单个答案为 solve(i,r)−solve(i,l−1)solve (i, r) - solve (i, l - 1)solve(i,r)−solve(i,l−1)。
solvesolvesolve 的求法不难想到容斥原理。
令 x=p1q1p2q2...pnqnx = p_1^{q_1} p_2^{q_2} ... p_n^{q_n}x=p1q1p2q2...pnqn
则 [1,y][1, y][1,y] 中与 xxx 互质的数字共有:
y−∑i=1n⌊ypi⌋+∑i=1n∑j=i+1n⌊ypipj⌋−....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⌋−....
注意优化:
不能单独每个 xxx 去分解质因数,时间复杂度会变为 O(nn)O (n \sqrt {n})O(nn),所以我们可以用埃筛,用每个质数去筛,每个筛到的数在 vectorvectorvector 中记录下来这个质因数。
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;
}