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

4 篇文章 0 订阅

题目大意

最近, 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 a2n),那么 μ ( n ) = 0 \mu(n)=0 μ(n)=0。否则,不妨分解质因数 n = p 1 p 2 ⋯ p k n=p_1p_2\cdots p_k n=p1p2pk,然后 μ ( 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=1i!12.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)=dnμ(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)} ednμ(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)}} dn(dn)μ(d)=dndμ(d)dnnμ(d)

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

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

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

因为 ∑ d ∣ n μ ( d ) = 0 \sum_{d|n}\mu(d) = 0 dnμ(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=p1k1p2k2pmkm

由于莫比乌斯函数的定义,所以 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=1mpij=0k1Ck1j(1)j1

这一步本质上就是将 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=1mpij=0k1Ck1j(1)j11=i=1mpij=0k1Ck1j(1)j

我们考虑 ∑ j = 0 k − 1 C k − 1 j ( − 1 ) j \sum_{j=0}^{k-1} C_{k-1}^j(-1)^{j} j=0k1Ck1j(1)j,这个式子只有当 k − 1 > 0 k-1>0 k1>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

代码

#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 ");
    }
}
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值