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,l1)

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-.... yi=1npiy+i=1nj=i+1npipjy....

注意优化:
不能单独每个 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;
}
posted @ 2021-06-19 23:15  C2022lihan  阅读(52)  评论(0编辑  收藏  举报