【JZOJ5663】呼吸决定

该博客讨论了如何使用杜教筛(Atkin-Bernstein Sieve)解决数论中的一个题目,涉及自然数幂和及拉格朗日插值法。作者提到由于空间限制,需要对算法进行优化,通过预处理一定数量的自然数幂和来降低空间复杂度,以适应题目要求的时间和内存限制。
摘要由CSDN通过智能技术生成

Time Limits: 1000 ms Memory Limits: 30760 KB

Description
Desc

Input
In

Output
Out

Sample Input

10 3

Sample Output

714

Data Constraint
Data


analysis

这。。。空间有点醉

杜教筛的常用套路
f[n]=ni=1imμ(i) f [ n ] = ∑ i = 1 n i m μ ( i )
首先有 ni=1imd|iμ(d)=1 ∑ i = 1 n i m ∑ d | i μ ( d ) = 1

i=1nimd|iμ(d)=d=1i=1nddmimμ(d)=i=1nimd=1nidmμ(d)=i=1nimf(ni)=1(588)(589)(590) (588) ∑ i = 1 n i m ∑ d | i μ ( d ) = ∑ d = 1 ∑ i = 1 ⌊ n d ⌋ d m i m μ ( d ) (589) = ∑ i = 1 n i m ∑ d = 1 ⌊ n i ⌋ d m μ ( d ) (590) = ∑ i = 1 n i m f ( ⌊ n i ⌋ ) = 1

移项得 f(n)=1ni=2imf(ni) f ( n ) = 1 − ∑ i = 2 n i m f ( ⌊ n i ⌋ )

于是可以套杜教筛,这需要自然数幂和,拉格朗日插值法

能过?想太多,直接拉格朗日肯定过不了,再加上卡空间。。。

杜教筛只用预处理1e6个
在预处理5e6个自然数幂和,其余的自然数幂和也只不过 n/5e6 n / 5 e 6 个,预处理拉格朗日插值法即可。

注意拉格朗日要m+2个量

时间复杂度 O(n23+nm5106) O ( n 2 3 + n m 5 ∗ 10 6 ) ,空间卡过

#include<cstring>
#include<cstdio>
#define N 1000000
#define ll long long
#define lim 10007
#define mo 998244353

using namespace std;

int mu[N+1],mp[N*5+1],pr[100100],f[N+1],hash[lim][2],n,m,init[201];
int tmp[100010],pre[100010],suf[100010],ifr[100010];
bool bz[N+1];

int qpow(int a,int i){
    int r=1;for(;i;i>>=1,a=(ll)a*(ll)a%mo)if(i&1)r=(ll)r*(ll)a%mo;return r;
}

int get(int x){
    int y=x%lim;
    while(hash[y][0] && hash[y][0]!=x)y=(y+1)%lim;return y;
}

int mpow(int x){
    if(x<=5*N)return mp[x];
    return init[n/x];
}

int query(int n){
    if(n<=N)return f[n];
    int x=get(n);
    if(hash[x][0]==n)return hash[x][1];
    hash[x][0]=n;int ans=1;
    for(int i=2,j,las=1;i<=n;i=j+1){
        j=n/(n/i);int k=mpow(j);
        ans=((ll)ans+(ll)mo-((ll)mo+(ll)k-(ll)las)%mo*query(n/i)%mo)%mo;
        las=k;
    }hash[x][1]=ans;return ans;
}

int main(){
    scanf("%d %d",&n,&m);
    f[1]=1;mp[1]=1;
    for(int i=2;i<=N*5;i++){
        if(mp[i]==0)mp[i]=qpow(i,m);
        if(i<=N){
            if(!bz[i])pr[++pr[0]]=i,mu[i]=mo-1;
            f[i]=((ll)f[i-1]+(ll)mu[i]*(ll)mp[i])%mo;
        }
        for(int j=1;j<=pr[0] && i*pr[j]<=5*N;j++){
            if(i*pr[j]<=N)bz[i*pr[j]]=1,mu[i*pr[j]]=mo-mu[i];
            mp[i*pr[j]]=(ll)mp[i]*(ll)mp[pr[j]]%mo;
            if(i%pr[j]==0){if(i*pr[j]<=N)mu[i*pr[j]]=0;break;}
        }
    }
    for(int i=2;i<=5*N;i++)mp[i]=(mp[i]+mp[i-1])%mo;
    ifr[0]=1;
    ifr[m+1]=1;for(int i=2;i<=m+1;i++)ifr[m+1]=(ll)ifr[m+1]*(ll)i%mo;ifr[m+1]=qpow(ifr[m+1],mo-2);
    for(int i=m;i;i--)ifr[i]=(ll)ifr[i+1]*(ll)(i+1)%mo;
    for(int j=1;n/j>N*5;j++){
        int x=n/j;init[j]=0;
        for(int i=0;i<=m+1;i++)tmp[i]=x-i;pre[0]=tmp[0];suf[m+1]=tmp[m+1];
        for(int i=1;i<=m+1;i++)pre[i]=(ll)pre[i-1]*(ll)tmp[i]%mo;
        for(int i=m;i>=0;i--)suf[i]=(ll)suf[i+1]*(ll)tmp[i]%mo;
        for(int i=0;i<=m+1;i++)init[j]=((ll)init[j]+(ll)mp[i]*(ll)(i?pre[i-1]:1)%mo*(ll)((i<1+m)?suf[i+1]:1)%mo*(ll)ifr[i]%mo*(ll)ifr[m+1-i]%mo*((((m+1-i)&1)==0)?1:mo-1))%mo;
    }
    printf("%d",query(n));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值