[欧拉函数+推式子+NTT]2019ICPC南京网络赛 C Tsy's number 5

1 篇文章 0 订阅
1 篇文章 0 订阅

https://nanti.jisuanke.com/t/41300

题意:对1e5范围内的n求以上这么个东西%998244353

我太难了。这个题太难了。

前半部分是题解的内容,我作为一个数论菜鸡琢磨了一琢磨也看懂了,就是不会NTT的我后面看不懂,问过大佬之后写了点解释咳咳。

fi表示的是欧拉函数值==i的数的个数

然后,对于一个i,绿色的部分是好求的,然后就是求红色圈出来的部分

我们假设有一个多项式A,它的系数 a_{i}=i * f_{i} * \sqrt{2}^{i^{2}}

还有另一个多项式B,它的系数 b_{i}=\sqrt{2}^{-i^{2}}

经思索(所以到底是怎么思索出来的啊喂),多项式A*B的第i次项系数c_{i}=\sum_{j=0}^{i}a_{j}*b_{i-j}=\sum_{j=0}^{i}j*f_{j}*\sqrt{2}^{j^{2}}*\sqrt{2}^{-(i-j)^{2}}

然而j=0的时候aj=0,所以j直接从1开始求和也是可以的,这样的话就和红圈部分长得一毛一样了

所以最终结果=2*\sum_{i=1}^{n}a_{i}*c_{i}-\sum_{i=1}^{n}a_{i}^{2}

这个c用NTT算,其他的直接算。

另外,经观察,根号2的指数都是偶数,所以最后实际上用到的可以理解为根号2的平方(就是在指数中提出一个2,然后根号2平方一下)。根号2不好算,所以我们暴力找到一个数x,使得x^2%mod==2,然后用这个数代替根号2去计算就好啦

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e5+5;
const ll mo=998244353;
ll fpow(ll a, ll b){
    ll ans=1;
    while(b>0){if(b&1)ans=ans*a%mo;b>>=1;a=a*a%mo;}
    return ans;
}
ll D(ll x) {
    ((x>=mo) && (x-=mo)) || ((x<0) && (x+=mo));
    return x;
}
void NTT(ll a[],ll n,ll op) {
    for(ll i=1,j=n>>1;i<n-1;++i) {
        if(i<j) swap(a[i],a[j]);
        ll k=n>>1;
        while(k<=j) {
            j-=k;
            k>>=1;
        }
        j+=k;
    }
    for(ll len=2;len<=n;len<<=1) {
        ll rt=fpow(3,(mo-1)/len);
        for(ll i=0;i<n;i+=len) {
            ll w=1;
            for(ll j=i;j<i+len/2;++j) {
                ll u=a[j],t=1LL*a[j+len/2]*w%mo;
                a[j]=D(u+t),a[j+len/2]=D(u-t);
                w=1LL*w*rt%mo;
            }
        }
    }
    if(op==-1) {
        reverse(a+1,a+n);
        ll in=fpow(n,mo-2);
        for(ll i=0;i<n;++i)
            a[i]=1LL*a[i]*in%mo;
    }
}
void conv(ll* a,ll h1,ll* b,ll h2,ll* c) {//h是最高次项次数
    ll s=1;
    while(s<h1+h2+1)s<<=1;
    NTT(a,s,1);NTT(b,s,1);
    for(ll i=0;i<s;++i)
        c[i]=1LL*a[i]*b[i]%mo;
    NTT(c,s,-1);
   // NTT(a,n,-1);
}
ll qp(ll a,ll b,ll mod){
    a%=mod;
    ll ans=1;
    while(b){
        if(b&1) ans=(ans*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return ans;
}
ll a[maxn],b[maxn],c[maxn],d[maxn],p[maxn],f[maxn];
ll primes[maxn],phi[maxn],fl[maxn],cnt;
int main(){
    int t,n;
    scanf("%d",&t);
    memset(fl,1,sizeof(fl));
    fl[1]=0;
    phi[1]=1;
    for (int i=2;i<=1e5;i++){
        if (fl[i]) primes[++cnt]=i,phi[i]=i-1;
        for (int j=1;j<=cnt && i*primes[j]<=1e5;j++){
            fl[i*primes[j]]=0;
            if (i%primes[j]==0) {phi[i*primes[j]]=phi[i]*primes[j]; break;}
            else phi[i*primes[j]]=phi[i]*phi[primes[j]];
        }
    }
    while(t--){
        scanf("%d",&n);
        memset(f,0,sizeof(f));
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        for(ll i=1;i<=n;i++) f[phi[i]]++;
        for(ll i=0;i<=n;i++){
            d[i]=a[i]=i*f[i]%mo*qp(116195171ll,i*i%(mo-1),mo)%mo,b[i]=qp(116195171ll,-i*i%(mo-1)+mo-1,mo);
        }
        conv(a,n,b,n,c);//此时c中位各次系数,从低到高
        ll ans=0;
        for(ll i=1;i<=n;i++){
            ans=(ans+2*d[i]%mo*c[i]%mo-d[i]*d[i]%mo)%mo;
        }
        ans=(ans+mo)%mo;
        printf("%lld\n",ans);

    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值