莫比乌斯&线性筛

最难受的莫过于正式赛因为平常不训练,训练懒就不写博客导致莫比乌斯忘记怎么做了???菜是原罪,nothing to say。

抢救一波线性筛(有生之年还能用上吗):

const int MAX = 1e6+10;
const ll mod = 1e9+7;
ll mob[MAX],d[MAX],facnum[MAX],sum[MAX],p[MAX],f[MAX];
bool noprime[MAX];
void get_all()  
{  
    int pnum = 0;  
    phi[1] = 1;  //欧拉 
    mob[1] = 1;  //莫比乌斯 
    facnum[1] = 1;  //小于自己的约数个数 
    for(int i = 2; i < MAX; i++)  
    {  
        if(!noprime[i])  
        {  
            phi[i] = i - 1;  
            mob[i] = -1;  
            p[pnum ++] = i;     
            facnum[i] = 2;    
            d[i] = 1;        
        }  
        for(int j = 0; j < pnum && i * p[j] < MAX; j++)  
        {  
            noprime[i * p[j]] = true;  
            if(i % p[j] == 0)  
            {  
                phi[i * p[j]] = phi[i] * p[j];  
                mob[i * p[j]] = 0;  
                facnum[i * p[j]] = facnum[i] / (d[i] + 1) * (d[i] + 2);   
                d[i * p[j]] = d[i] + 1;   
                break;  
            }  
            phi[i * p[j]] = phi[i] * (p[j] - 1);  
            mob[i * p[j]] = -mob[i];  
            facnum[i * p[j]] = facnum[i] * 2;  
            d[i * p[j]] = 1;   
        }  
    }  
}  
以HDU-6134为例,给出常见解题方案:

g(i)=nj=1ij , h(i)=nj=1ij[(i,j)=1]  
那么 g(i)=d|ih(d)  
//求解 g(i) 我是枚举 j(2<=j<=N) ij ,然后可以知道 [(j1)i+1,ji] 这段区间是可以一起处理的,因为这段区间除以 j 后的结果都一样。 
我们只需 g((j1)i+1)+=j,g(ji+1)=j ,然后前缀和就是要求的 g(i)

现在我们已经知道了 g(i) ,要求 h(i) 。我们可以用莫比乌斯反演。 
可知 h(i)=d|iμ(d)g(i/d)  
如果你直接两个for循环枚举 i i 的因子是会T的。 
这里我们第一个for可以枚举 d ,然后第二个for枚举 i 每次加 d ,可以在 O(nlogn) 的时间内处理出来(应该是这个复杂度。。) 
然后对 h(i) 求前缀和就是 f(i)  
然后 O(1) 查询


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
const int MAX = 1e6+10;
const ll mod = 1e9+7;
ll mob[MAX],d[MAX],facnum[MAX],sum[MAX],p[MAX],f[MAX];
bool noprime[MAX];
void get_all()
{
    memset(noprime,false,sizeof(noprime));
    int pnum = 0;
    //phi[1] = 1;
    mob[1] = 1;
    sum[1]=1;
    facnum[1] = 1;
    for(int i = 2; i < MAX; i++)
    {
        if(!noprime[i])
        {
            //phi[i] = i - 1;
            mob[i] = -1;
            p[pnum ++] = i;
            facnum[i] = 2;
            d[i] = 1;
        }
        for(int j = 0; j < pnum && i * p[j] < MAX; j++)
        {
            noprime[i * p[j]] = true;
            if(i % p[j] == 0)
            {
                //phi[i * p[j]] = phi[i] * p[j];
                mob[i * p[j]] = 0;
                facnum[i * p[j]] = facnum[i] / (d[i] + 1) * (d[i] + 2);
                d[i * p[j]] = d[i] + 1;
                break;
            }
            //phi[i * p[j]] = phi[i] * (p[j] - 1);
            mob[i * p[j]] = -mob[i];
            facnum[i * p[j]] = facnum[i] * 2;
            d[i * p[j]] = 1;
        }
    }
    f[1]=1;
    sum[1]=1;
    for(int i=2;i<MAX;i++)
    {
        f[i]=(f[i-1]+facnum[i-1]+1)%mod;
    }
    for(int i=2;i<MAX;i++)
    {
        mob[i]=(mob[i]+mob[i-1])%mod;
        sum[i]=(sum[i-1]+f[i])%mod;
    }
}
int main()
{
    int n;
    get_all();
    while(scanf("%d",&n)!=EOF)
    {
        ll ans=0;
        int last;
        for(int i=1;i<=n;i=last+1)
        {
            last = n/(n/i);
            ans=(ans+(mob[last]-mob[i-1]+mod)%mod*(sum[n/i]%mod))%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值