【Coel.个人题解】【首次出题w】高考数学

题前闲话

很高兴能够给模拟赛出一道题目,感谢 Sherlockk 的支持w
虽然这道题甚至都还没有用上(本题解撰写于 6 月 19 日),不过我提前码上来应该没人发现吧(小声
直接隐藏不就好了吗

题目描述

给出两个正整数 n,m,随机从 nm 的整数中选取 2 个不同的整数,求出这 2 个数互质的概率,并用分数形式表示。

两个数互质定义为两个数除了 1 以外,没有其它的公约数。

解题思路

三步走:算分子,算分母,然后约分。
nm 可选数字个数为 k ,可以知道 k=mn+1,那么分母就是 Ck2
接下来推推式子:

Ck2=k!2!(k2)!=k(k1)(k2)...×2×12(k2)(k3)...×2×1=k(k1)2

这样就能以 O(1) 的复杂度求出分母。
约分相信大家小学时就已经学过,是把分子分母同时除以它们的最大公约数。algorithm 头文件里提供了一个函数 __gcd(n,m) 可以求最大公约数,如果不会辗转相除法可以用这个。
接下来是重点:怎么求分子?
这就要用到莫比乌斯反演了。
考察我们要求的分子,其实可以表示为:

i=nmj=i+1m[gcd(i,j)=1]

中括号表示内部结果正确则结果为 1 ,反之为 0
上式其实就代表了暴力求解的过程。
这个式子不太好化(因为 j 的起始值不定),但不妨换个方向,原式可以改写成:

i=nmj=nm[gcd(i,j)=1]2

上式是说,让 i,j 都从 n 开始枚举。这样做会重复算,除以二就好了。
再根据容斥原理,我们可以把式子分成四份处理,这一点类似于二维前缀和的计算方法。这里只给出结论:四份对应的式子为

S1=12i=1mj=1m[gcd(i,j)=1]

S2=12i=1n1j=1m[gcd(i,j)=1]

S3=12i=1mj=1n1[gcd(i,j)=1]

S4=12i=1nj=1n[gcd(i,j)=1]

结果为 S1S2S3+S4
显然,这四个式子都可以看做如下形式:

12i=1aj=1b[gcd(i,j)=1]

那么我们就以这个形式为例进行推导。
由于 gcd=1 的时候才会对原式产生贡献,这与狄利克雷卷积单位元的定义(ε(n)=1 当且仅当 n=1,否则 ε(n)=0)是一致的。所以将其代入:

12i=1aj=1bε(gcd(i,j))

利用单位元的性质将其展开:

12i=1aj=1bdgcd(i,j)μ(d)

dgcd(i,j) 表示枚举所有能够整除 gcd(i,j) 的值 d
顺带一提,

[gcd(i,j)=1]=dgcd(i,j)μ(d)

就是莫比乌斯反演的结论。这个结论也可以不使用单位元而证明出,此处不再详述,感兴趣的同学可以试着推导一下。
改变一下求和顺序,把枚举符合条件的 d 放在式子前面进行:

12d=1μ(d)i=1a[di]j=1b[dj]

最后,由于 [1,m]d 的倍数有 md 个( 表示向下取整),进一步化简:

12d=1min(a,b)μ(d)adbd

呼……式子终于化完了!
那么怎么计算呢?
首先 μ(d) 是一个积性函数,可以用类似筛质数的方法全部预处理出来,这个过程的时间复杂度是 O(n) 的。
接下来,对于上面的式子如果直接暴力代入求和,处理单个数据的时间复杂度为 O(m) (这里忽略掉了求最大公约数的时间复杂度),理论上无法通过(然而还是被 Sherlockk 卡过了……)
可以使用一种特殊的数据结构:数论分块。它的原理是把相同的 nd 合在一起计算,把时间复杂度压缩到 O(m)
为了节省篇幅,这里只给出预处理 μ(d) 的代码以及代式子的代码供参考:

void init() { //预处理莫比乌斯函数
    int tot = 0;
    mu[1] = 1;
    for (int i = 2; i <= maxn - 10; i++) {
        if (!vis[i]) {
            p[++tot] = i;
            mu[i] = -1;
        }
        for (int j = 1; j <= tot && i * p[j] <= maxn - 10; j++) {
              vis[i * p[j]] = 1;
              if (i % p[j] == 0) {
                  mu[i * p[j]] = 0;
                  break;
              }
              mu[i * p[j]] = -mu[i];
        }
    }
    for (int i = 1; i <= maxn - 10; i++)
        mu[i] += mu[i - 1];
}

int solve(int n, int m) { //推出的式子
    int res = 0;
    for (int i = 1, j; i <= min(n, m); i = j + 1) {
        j = min(n / (n / i), m / (m / i));
        res += (mu[j] - mu[i - 1]) * (n / i) * (m / i);
    }
    return res;
}

结语

第一次出题感觉有点毒瘤,如果影响到了大家的比赛体验,那只能说句抱歉了qwq
后记:实际上区间中位数比这个毒瘤一点。

posted @   秋泉こあい  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示