HDU - 7318 Guess( 2023“钉耙编程”中国大学生算法设计超级联赛第四场 G)

题目大意

最近, Stump \text{Stump} Stump 推导出了 ∑ k = 1 n μ 2 ( k ) = ∑ k = 1 μ ( k ) ⌊ n k 2 ⌋ \sum_{k=1}^n\mu^2(k)=\sum_{k=1}\mu(k)⌊\frac{n}{k^2}⌋ ∑k=1nμ2(k)=∑k=1μ(k)⌊k2n⌋,这震惊了 Yoshinow2001 \text{Yoshinow2001} Yoshinow2001 一整年。

上面的 μ \mu μ 是莫比乌斯函数:如果 n n n 包含平方因子(即有正整数 a > 1 a>1 a>1 使 a 2 ∣ n a^2|n a2∣n),那么 μ ( n ) = 0 \mu(n)=0 μ(n)=0。否则,不妨分解质因数 n = p 1 p 2 ⋯ p k n=p_1p_2\cdots p_k n=p1p2⋯pk,然后 μ ( n ) = ( − 1 ) k \mu(n)=(-1)^k μ(n)=(−1)k。例如: μ ( 1 ) = 1. μ ( 2 ) = μ ( 3 ) = − 1 \mu(1)=1.\mu(2)=\mu(3)=-1 μ(1)=1.μ(2)=μ(3)=−1。

回想一下 ln ⁡ ( n ) \ln(n) ln(n) 表示以 e e e 为底的 n n n 的对数,其中 e = ∑ i = 1 ∞ 1 i ! ≈ 2.71828 e=\sum_{i=1}^\infty\frac{1}{i!}\approx 2.71828 e=∑i=1∞i!1≈2.71828。

现在 Yoshinow2001 \text{Yoshinow2001} Yoshinow2001 非常愤怒,提出了一个问题!

让:

S ( n ) = ∑ d ∣ n μ ( n d ) ln ⁡ ( d ) S(n)=\sum_{d|n}\mu(\dfrac{n}{d})\ln(d) S(n)=d∣n∑μ(dn)ln(d)

你需要计算:

e S ( n ) m o d    998244353 e^{S(n)} \mod 998244353 eS(n)mod998244353

当 Stump \text{Stump} Stump 看到题目时,他气坏了!他没有推出 O ( 1 ) O(1) O(1) 的判断规律。

现在你要告诉他什么叫做大佬,写出一个程序在 5 s 5s 5s 内爆切这个问题吧!

思路

我们先把 S ( n ) S(n) S(n) 代入进去,我们要求的式子变为如下形式:

e ∑ d ∣ n μ ( n d ) ln ⁡ ( d ) e^{\sum_{d|n}\mu(\dfrac{n}{d})\ln(d)} e∑d∣nμ(dn)ln(d)

我们知道 e l n ( n ) = n e^{ln(n)}=n eln(n)=n,则我们可以将式子化为如下形式:

∏ d ∣ n ( n d ) μ ( d ) = ∏ d ∣ n n μ ( d ) ∏ d ∣ n d μ ( d ) \prod_{d|n}(\dfrac{n}{d})^{\mu(d)}=\dfrac{\prod_{d|n} n^{\mu(d)}}{\prod_{d|n} d^{\mu(d)}} d∣n∏(dn)μ(d)=∏d∣ndμ(d)∏d∣nnμ(d)

化为两个式子后我们分别来看,先看上面的式子。

由于底数不变,则式子可以写成如下形式:

n ∑ d ∣ n μ ( d ) n^{\sum_{d|n}\mu(d)} n∑d∣nμ(d)

因为 ∑ d ∣ n μ ( d ) = 0 \sum_{d|n}\mu(d) = 0 ∑d∣nμ(d)=0(莫比乌斯函数的性质),所以这个式子的值就为 1 1 1。

再来看下面的式子。

我们对于 n n n 质因数分解,得 n = p 1 k 1 p 2 k 2 ⋯ p m k m n=p_1^{k_1}p_2^{k_2}\cdots p_m^{k_m} n=p1k1p2k2⋯pmkm。

由于莫比乌斯函数的定义,所以 d d d 不可能包含重复的质因数。

于是我们考虑每个质因数的贡献,可以列出如下式子。

∏ i = 1 m p i ∑ j = 0 k − 1 C k − 1 j ( − 1 ) j − 1 \prod_{i=1}^mp_i^{\sum_{j=0}^{k-1} C_{k-1}^j(-1)^{j-1}} i=1∏mpi∑j=0k−1Ck−1j(−1)j−1

这一步本质上就是将 d d d 质因数分解,由于连乘可以将指数相加,得出以上式子。

则原式变为:

1 ∏ i = 1 m p i ∑ j = 0 k − 1 C k − 1 j ( − 1 ) j − 1 = ∏ i = 1 m p i ∑ j = 0 k − 1 C k − 1 j ( − 1 ) j \dfrac{1}{\prod_{i=1}^mp_i^{\sum_{j=0}^{k-1} C_{k-1}^j(-1)^{j-1}}}=\prod_{i=1}^mp_i^{\sum_{j=0}^{k-1} C_{k-1}^j(-1)^{j}} ∏i=1mpi∑j=0k−1Ck−1j(−1)j−11=i=1∏mpi∑j=0k−1Ck−1j(−1)j

我们考虑 ∑ j = 0 k − 1 C k − 1 j ( − 1 ) j \sum_{j=0}^{k-1} C_{k-1}^j(-1)^{j} ∑j=0k−1Ck−1j(−1)j,这个式子只有当 k − 1 > 0 k-1>0 k−1>0,即 k > 1 k>1 k>1 的时候才为 1 1 1,其余时候皆为 0 0 0。

所以只有当 n n n 只有一种质因子时,答案为 n n n 的质因子,否则答案为 1 1 1。

我们可以枚举 n n n 开 k k k 次方后的数,在判断这个数 k k k 次方等不等于 n n n,再用 miller_rabin \text{miller\_rabin} miller_rabin 来判断是否为质数,如果为质数,则输出该质数。

如果找不到满足条件的质数,则输出 1 1 1。

代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int T, bz[1000005], pn;
LL n, m, ans, p[500000];
__int128 Pow(__int128 a, __int128 b, __int128 MOD) {
    __int128 s = 1;
    while (b) {
        if (b & 1)
            s = s * a % MOD;
        a = a * a % MOD, b = b >> 1;
    }
    return s;
}
bool miller_rabin(LL x) {
    if (x < 2)
        return false;
    for (int i = 1; i <= pn; i++) {
        if (p[i] == x)
            return true;
        if (x % p[i] == 0)
            return false;
        if (Pow(p[i], x - 1, x) != 1)
            return false;
        LL k = x - 1, t;
        while (k % 2 == 0) {
            k = k / 2, t = Pow(p[i], k, x);
            if (t != 1 && t != x - 1)
                return false;
            if (t == x - 1)
                return true;
        }
    }
    return true;
}
int main() {
    for (int i = 2; i <= 1000000; i++) {
        if (!bz[i])
            p[++pn] = i;
        for (int j = 2; j * i <= 1000000; j++) bz[j] = 1;
    }
    scanf("%d", &T);
    while (T--) {
        scanf("%lld", &n), ans = n;
        if (n == 1) {
            printf("1 ");
            continue;
        }
        bool flag = false;
        for (int i = 1; i <= 64; i++) {//注意精度问题,我们可以将开i次方后的数+1或-1再尝试一下
            LL t = pow((long double)n, (long double)1.0 / i);
            if (miller_rabin(t) && Pow(t, i, n + 1) == n) {
                flag = true, printf("%lld ", t % 998244353);
                break;
            }
            t = pow((long double)n, (long double)1.0 / i) + 1;
            if (miller_rabin(t) && Pow(t, i, n + 1) == n) {
                flag = true, printf("%lld ", t % 998244353);
                break;
            }
            t = pow((long double)n, (long double)1.0 / i) - 1;
            if (miller_rabin(t) && Pow(t, i, n + 1) == n) {
                flag = true, printf("%lld ", t % 998244353);
                break;
            }
        }
        if (!flag)
            printf("1 ");
    }
}
相关推荐
悄悄敲敲敲4 小时前
C++:dfs习题四则
c++·算法·深度优先
安於宿命5 小时前
【Linux】进程间通信——进程池
linux·c++
南郁10 小时前
001-监控你的文件-FSWatch-C++开源库108杰
c++·开源·文件系统·文件监控·fswatch·文件变动信息·libfswatch
linux开发之路11 小时前
C++Linux进阶项目分析-仿写Redis之Qedis
linux·c++·redis·多线程·后端开发
EPSDA11 小时前
Linux线程库与线程库封装
linux·运维·服务器·开发语言·c++
孤独得猿11 小时前
排序算法复习——包括插入排序、希尔排序、冒泡排序、快排(包括霍尔法、挖坑法、快慢指针法)、堆排、选择排序、归并排序等 (代码采用c/c++混编)
c语言·数据结构·c++·笔记·算法·排序算法
编程探索者小陈11 小时前
【C++】智能指针的使用及其原理
开发语言·c++
半桔13 小时前
贪吃蛇解析
c语言·开发语言·数据结构·c++·算法
Pan_peter13 小时前
零基础学QT、C++(一)安装QT
c++·qt
万能的小裴同学14 小时前
MFC 自定义十六进制显示控件
开发语言·c++·mfc