hdu5628 : Clarke and math(线性筛)

这题以前用快速幂写的
今天用线性筛写了一下,真刺激
大概就是说那个东西是
f1k f ∗ 1 k
然后你只要算出来 1k 1 k
就可以 O(nlogn) O ( n l o g n ) 卷积了
关于算 1k 1 k 当然可以直接快速幂
O(nlognlogk+nlogn) O ( n l o g n l o g k + n l o g n )
然而有更好的做法,我们可以线性筛
首先因为 1 1 是积性函数
所以F=1k也是积性函数
所以我们只需要考虑 F(pr) F ( p r ) 的取值就好了
我们可以发现每个 p p 都是一样的
所以就相当于在一个k(r+1)的网格上走
(1,1) ( 1 , 1 ) (k,r+1) ( k , r + 1 ) 的方案数为 C(k+r1,r) C ( k + r − 1 , r )
然后就可以处理一下逆元直接筛了
代码:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
inline int read(){
    int x=0,f=1;char ch=' ';
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return f==1?x:-x;
}
const int N=1e5+5,mod=1e9+7;
inline void inc(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int cnt,prime[N],vis[N],e[N];
int F[N],f[N],g[N],inv[N];
int main(){
    int T=read();
    for(int S=1;S<=T;++S){
        memset(e,0,sizeof e);
        memset(vis,0,sizeof vis);
        memset(g,0,sizeof g);
        int n=read(),k=read();F[1]=1;
        inv[0]=inv[1]=1;cnt=0;
        for(int i=2;i<=n;++i)inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
        for(int i=2;i<=n;++i){
            if(!vis[i]){
                prime[++cnt]=i;
                F[i]=k;
                e[i]=1;
            }
            for(int j=1;j<=cnt && i*prime[j]<=n;++j){
                vis[i*prime[j]]=1;
                if(i%prime[j]==0){
                    F[i*prime[j]]=1LL*F[i]*inv[e[i]+1]%mod*(k+e[i])%mod;
                    e[i*prime[j]]=e[i]+1;
                    break;
                }
                else{
                    F[i*prime[j]]=1LL*F[i]*F[prime[j]]%mod;
                    e[i*prime[j]]=1;
                }
            }
        }
        for(int i=1;i<=n;++i)f[i]=read();
        for(int i=1;i*i<=n;++i){
            inc(g[i*i],1LL*f[i]*F[i]%mod);
            for(int j=i+1;i*j<=n;++j){
                inc(g[i*j],1LL*f[i]*F[j]%mod);
                inc(g[i*j],1LL*f[j]*F[i]%mod);
            }
        }
        for(int i=1;i<n;++i)printf("%d ",g[i]);
        printf("%d\n",g[n]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值