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 ");
    }
}
相关推荐
__AtYou__3 小时前
Golang | Leetcode Golang题解之第557题反转字符串中的单词III
leetcode·golang·题解
李元豪3 小时前
【智鹿空间】c++实现了一个简单的链表数据结构 MyList,其中包含基本的 Get 和 Modify 操作,
数据结构·c++·链表
UestcXiye4 小时前
《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项
c++·计算机网络·ip·tcp
一丝晨光5 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc
丶Darling.5 小时前
Day40 | 动态规划 :完全背包应用 组合总和IV(类比爬楼梯)
c++·算法·动态规划·记忆化搜索·回溯
奶味少女酱~5 小时前
常用的c++特性-->day02
开发语言·c++·算法
我是哈哈hh6 小时前
专题十八_动态规划_斐波那契数列模型_路径问题_算法专题详细总结
c++·算法·动态规划
_小柏_7 小时前
C/C++基础知识复习(15)
c语言·c++
_oP_i7 小时前
cmake could not find a package configuration file provided by “Microsoft.GSL“
c++
mingshili8 小时前
[python] 如何debug python脚本中C++后端的core dump
c++·python·debug