【容斥原理/莫比乌斯反演】破译密码

重学了一下莫反,感觉收获了好多。(原来你还记得自己学过莫反

原题链接:https://www.acwing.com/problem/content/217/
题目描述

达达正在破解一段密码,他需要回答很多类似的问题:
对于给定的整数 a,b 和 d,有多少正整数对 x,y,满足 x≤a,y≤b,并且 gcd(x,y)=d。作为达达的同学,达达希望得到你的帮助。

我们分析一下,题目很明了,要求的就是 g c d ( x , y ) = d gcd(x , y) = d gcd(x,y)=d的个数,转化一下:
x ′ = x / d , y ′ = y / d [ g c d ( x , y ) = d ] = [ g c d ( x ′ , y ′ ) = 1 ] x' = x / d , y' = y / d\\ [gcd(x , y) = d] = [gcd(x' , y') = 1]\\ x=x/d,y=y/d[gcd(x,y)=d]=[gcd(x,y)=1]
这两个是等价的,但是看的时候我还是愣了一下(还是比较笨的),但是仔细思考下,感觉还是很容易证明的。

证明:
首先,x , y 的最大公约数是d,那么我们除去最大公约数,两个数就互质了,这个毋庸置疑,对于每个 g c d ( x , y ) = d gcd(x , y) = d gcd(x,y)=d都如此操作,那么可以证明,这两个的数量是一一对应的。
(这么显然,我还要思考一会,真滴笨!OVQ)


那么我们的问题转化为: x ≤ a / d x \le a/d xa/d , y ≤ b / d y \le b/d yb/d g c d ( x , y ) = 1 gcd(x , y)= 1 gcd(x,y)=1的个数,我们考虑容斥原理:互质的数 = 总数 - 不是互质的数,
总数很容易, a ′ = a / d , b ′ = b / d a' = a / d , b' = b / d a=a/d,b=b/d , 那么总数就是 a ′ ∗ b ′ a' * b' ab,下面分析下我们要减去的不互质的数:
∑ i = 2 m i n ( a , b ) [ a ′ i ] ∗ [ b ′ i ] ∗ m o b i u s [ i ] ∑^{min(a,b)}_{i=2} [\frac{a'}{i}]∗[\frac{b'}{i}]∗mobius[i] i=2min(a,b)[ia][ib]mobius[i]
式子上午看的,人是上午没的,我直接喵喵喵?? 愣了一会,发现显然(我是笨比).

证明
我们要减去的是什么呢?是不互质的数,不互质数是什么呢?是最大公因数不为1的数,不为1,那可以为几呢?不是1就行(废话),我们枚举最大公因数不为1的个数即可。枚举每一个可能的最大公因数,并且计算个数。
当最大公因数是只有一个质因子的时候,我们可以列出:
[ a ′ 2 ] ∗ [ b ′ 2 ] + [ a ′ 3 ] ∗ [ b ′ 3 ] + . . . . . [\frac{a'}{2}] * [\frac{b'}{2}] + [\frac{a'}{3}] * [\frac{b'}{3}] + ..... [2a][2b]+[3a][3b]+.....
最大公因数是2的两对数,实际上就等2的倍数个数相乘, 这两个个数是等同的,同理3也是,但是我们要考虑重复的情况,如6,我们2,3,都枚举了一次,那么会重复,根据容斥原理,我们要减去,最后我们发现,其实符号就是 M o b i u s Mobius Mobius函数(这个证明是显然的)。以此类推,枚举到 a ′ a' a b ′ b' b中较小的就可以了,因为大于较小的,则式子为0,我们就能推出上面的式子了。(被自己蠢哭了)

那么答案呼之欲出:
a ′ b ′ + ∑ i = 2 m i n ( a , b ) [ a ′ i ] ∗ [ b ′ i ] ∗ m o b i u s [ i ] a'b' + ∑^{min(a,b)}_{i=2} [\frac{a'}{i}]∗[\frac{b'}{i}]∗mobius[i]\\ ab+i=2min(a,b)[ia][ib]mobius[i]

我们把这玩意合体一下
∑ i = 1 m i n ( a , b ) [ a ′ i ] ∗ [ b ′ i ] ∗ m o b i u s [ i ] ∑^{min(a,b)}_{i=1} [\frac{a'}{i}]∗[\frac{b'}{i}]∗mobius[i] i=1min(a,b)[ia][ib]mobius[i]

(当当当当!)

然后我们发现,高兴的还是有点早,每次都是 O ( n ) O(n) O(n),总体是 O ( n 2 ) O(n^2) O(n2)时间复杂度很大,我们忍受不了。

然后神奇的东西就出现了,我们称之为 “优雅的暴力——分块”
下面来分析下分块,这个还好!没怎么卡。

首先,我们发现式子里面有很多取整,那么实际上数据应该是一段一段的,我们就可以考虑通过这点来加速,既然这一段都是一样的,我们直接一段一段的算,发现效率可观,可以达到 O ( n n ) ) O(n\sqrt{n}) ) O(nn )),下面写下分块的原理,我们定义一个 get(x) 函数,这个函数和lowbit函数一样,虽然短小,但是精悍,他主要做的事是:传回当前一样的一段中,下标最大的那个,也就是 a g e t ( x ) + 1 \frac{a}{get(x) + 1} get(x)+1a就会变小。

int get(int n , int x) {
	return n / (n / x);
}

我们说这玩意想着就是一段一段的,那么到底多少呢?答案是: 2 a 2\sqrt{a} 2a

证明: 我们把1到n分成:1到 a \sqrt{a} a a + 1 \sqrt{a} + 1 a +1 n n n,我们发现 a 1 ∼ a \frac{a}{1 \sim \sqrt{a}} 1a a,只有 a \sqrt{a} a 项,并且 a a + 1 ∼ n \frac{a}{\sqrt{a} + 1 \sim n} a +1na取值都小于 a \sqrt{a} a ,综上所述,只有 2 a 2\sqrt{a} 2a 个。

下面证明一下: [ a x ] = [ a g e t ( x ) ] [\frac{a}{x}] =[\frac{a}{get(x)}] [xa]=[get(x)a]
g e t ( x ) = [ a [ a x ] ] ≤ [ a a x ] = x [ a x ] ≥ [ a g e t ( x ) ] get(x) = [\frac{a}{[\frac{a}{x}]}] \le [\frac{a}{\frac{a}{x}}] = x \\ [\frac{a}{x}] \ge [\frac{a}{get(x)}]\\ get(x)=[[xa]a][xaa]=x[xa][get(x)a]
[ a g e t ( x ) ] = [ a [ a [ a x ] ] ] ≥ [ a a [ a x ] ] = [ a x ] [\frac{a}{get(x)}] = [\frac{a}{ [\frac{a}{[\frac{a}{x}]}] }] \ge [\frac{a}{ \frac{a}{[\frac{a}{x}]}}] = [\frac{a}{x}] [get(x)a]=[[[xa]a]a][[xa]aa]=[xa]
因为 [ a x ] ≤ [ a g e t ( x ) ] [\frac{a}{x}] \le [\frac{a}{get(x)}] [xa][get(x)a] [ a x ] ≥ [ a g e t ( x ) ] [\frac{a}{x}] \ge [\frac{a}{get(x)}] [xa][get(x)a],那么 [ a x ] = [ a g e t ( x ) ] [\frac{a}{x}] = [\frac{a}{get(x)}] [xa]=[get(x)a]得证。’

还有一个需要证明,这个不证明,时间复杂度没法保证:
[ a x ] ≥ [ a g e t ( x ) + 1 ] [\frac{a}{x}] \ge [\frac{a}{get(x) + 1}] [xa][get(x)+1a]
设: a = k x + r , 1 ≤ r < x a = kx + r, 1 \le r < x a=kx+r,1r<x
即证明:
k ≥ [ a [ a k ] + 1 ] k ∗ [ [ a k ] + 1 ] ≥ a k \ge [\frac{a}{[\frac{a}{k}]+ 1}]\\ k*[ [\frac{a}{k}]+ 1] \ge a k[[ka]+1a]k[[ka]+1]a
设: a = p k + q , 1 ≤ q < k a = pk + q, 1 \le q < k a=pk+q,1q<k
得:
k ( p + 1 ) > p k + q k p + k > p k + q k > q k(p + 1) > pk + q\\ kp + k > pk + q\\ k > q k(p+1)>pk+qkp+k>pk+qk>q
得证.(呼)

那么我们就可以愉快的写代码了~
如下

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 50010;

int tt , n , a , b , d;
int primes[N] , mobius[N] , sum[N] , cnt;
bool st[N];

void init(int n) {
    mobius[1] = 1;
    for (int i = 2;i <= n;i ++) {
        if (!st[i]) {
            primes[cnt ++ ] = i;
            mobius[i] = -1;
        }
        for (int j = 0; primes[j] <= n / i;j ++) {
            st[primes[j] * i] = true;
            if(i % primes[j] == 0) {
                mobius[i * primes[j]] = 0;
                break;
            }
            mobius[i * primes[j]] = -mobius[i];
        }
    }
    for (int i = 1;i <= n;i ++ ) sum[i] = sum[i - 1] + mobius[i];
}

int get(int a , int x) {
    return a / (a / x);
}

int main() {
    init(N - 1);
    ios::sync_with_stdio(false);
    cin.tie(0); cin >> tt;
    for (int i = 0;i < tt;i ++) {
        LL ans = 0;
        cin >> a >> b >> d;
        a = a / d , b = b / d;
        n = min(a , b);
        for(int l = 1 , r;l <= n;l = r + 1) {
            r = min(n , min(get(a , l) , get(b , l)));
            ans += (sum[r] - sum[l - 1]) * (LL)(a / l) * (LL)(b / l);
        }
        printf("%lld\n" , ans);
    }
    return 0;
}

当然,我们也可以用莫比乌斯反演直接写出来公式
若 F ( n ) = ∑ n ∣ d f ( d ) , 则 f ( n ) = ∑ n ∣ d μ ( d n ) ∗ F ( d ) 若F(n) = \sum\limits_{n|d}f(d),则f(n)=\sum\limits_{n|d}\mu(\cfrac{d}{n})*F(d) F(n)=ndf(d),f(n)=ndμ(nd)F(d)
我们定义
F ( n ) = ∑ x = 1 a ∑ y = 1 b [ n ∣ ( x , y ) ] f ( n ) = ∑ x = 1 a ∑ y = 1 [ ( x , y ) = n ] F(n) = ∑_{x = 1} ^ a ∑_{y = 1} ^ b [n | (x , y)]\\ f(n)= ∑_{x = 1} ^ a∑_{y = 1}[(x , y) = n] \\ F(n)=x=1ay=1b[n(x,y)]f(n)=x=1ay=1[(x,y)=n]
可以直接反演出来:
∑ i = 1 m i n ( a , b ) [ a ′ i ] ∗ [ b ′ i ] ∗ m o b i u s [ i ] ∑^{min(a,b)}_{i=1} [\frac{a'}{i}]∗[\frac{b'}{i}]∗mobius[i] i=1min(a,b)[ia][ib]mobius[i]
数学,真是奇妙呢

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值