牛客NOIPtg day5 B-demo的gcd

一句话题意:给定长度为n的序列,求任意两两之间gcd的积mod 998244353的值。

好像是莫比乌斯反演板子题???(反正noip估计不考这种毒瘤

考场上想到一个类似正解的思路 好像摊下来最多处理nlogn次就义无反顾地写了结果爆零

(你以为for while if continue 都不要时间的啊

后来一看发现自己的思路貌似毫无问题,就是实现丑了(逃

 

可以看到对于每一个质数p,对答案的贡献为p^k(k-1) 其中k=序列中包含质因数p的数个数

然后对于质数的幂次p^q,考场上的思路是每次计算质数之后将原序列中的该质数除掉,如此循环直到找不到该质因数为止(于是就T了还不如暴力qvq

 

其实可以考虑另一种很简单的思路 对于任意一个p^k,枚举他的倍数,对答案的贡献即为p^k'(k'-1),与上述等价,复杂度为O(nlogn)(其实是ln

代码如下

#include <cstdio>
#include <iostream>
#define qvq register
#define int long long
using namespace std;
const int mod=998244353;
const int maxn=1000005;
int p[maxn],cnt,a[maxn];
bool np[maxn];

inline int read() {
    int x=0,f=1;
    char cr=getchar();
    while (cr>'9' || cr<'0') {
        if (cr=='-') f=-1;
        cr=getchar();
    }
    while (cr>='0' && cr<='9') {
        x=(x<<3)+(x<<1)+cr-'0';
        cr=getchar();
    }
    return x*f;
}

inline void euler() {
    for (qvq int i=2;i<=1000000;i++) {
        if (!np[i]) p[++cnt]=i;
        for (qvq int j=1;j<=cnt && p[j]*i<=1000000;j++) {
            np[p[j]*i]=1;
            if (i%p[j]==0) break;
        }
    }
}

inline int val(int k) {
    return k*(k-1)/2;
}

inline int power(int a,int b,int p) {
    if (b==0) return 1;
    if (b==1) return a;
    if (b&1) return a*power(a*a%p,b>>1,p)%p;
    else return power(a*a%p,b>>1,p);
}

int num[maxn],f[maxn];

signed main() {
    int n=read();
    int lim=-1;
    for (qvq int i=1;i<=n;i++) a[i]=read(),num[a[i]]++,lim=max(lim,a[i]);
    euler();
    for (int i=1;i<=cnt;i++) {
        int temp=p[i];
        while (temp<=1000000) {
            f[temp]=p[i];
            temp*=p[i];
        }
    }
    int ans=1;
    for (int i=1;i<=lim;i++) {
        if (!f[i]) continue;
        int res=0;
        for (int j=i;j<=lim;j+=i) res+=num[j];
        ans*=power(f[i],val(res),mod),ans%=mod;
    }
    printf("%lld",ans);
}

我太菜了orz

posted @ 2019-08-25 11:25  YoOXiii  阅读(129)  评论(1编辑  收藏  举报