BZOJ 4176 Lucas的数论

Posted on 2017-03-06 15:42  ziliuziliu  阅读(250)  评论(0编辑  收藏  举报

反演就告一段落啦。。。

这题主要是化式子需要灵活。。。主要有交换Σ,减小枚举范围两个技巧。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define maxn 5000050
#define mod 1000000007
using namespace std;
int n,miu[maxn],prime[maxn],tot=0,ans=0;
bool vis[maxn];
map <int,int> mp;
void get_table()
{
    miu[1]=1;
    for (int i=2;i<=maxn-50;i++)
    {
        if (!vis[i])
        {
            vis[i]=true;
            prime[++tot]=i;miu[i]=-1;
        }
        for (int j=1;j<=tot && i*prime[j]<=maxn-50;j++)
        {
             vis[i*prime[j]]=true;
             if (i%prime[j]) miu[i*prime[j]]=-miu[i];
             else {miu[i*prime[j]]=0;break;}
        }
    }
    for (int i=2;i<=maxn-50;i++) miu[i]+=miu[i-1];
}
int ask(int n)
{
    if (n<=maxn-50) return miu[n];
    if (mp.find(n)!=mp.end()) return mp[n];
    int ret=1,l=2,r;
    while (l<=n)
    {
        r=n/(n/l);
        ret=(ret-(long long)ask(n/l)*(r-l+1)+mod)%mod;
        l=r+1;
    }
    mp[n]=ret;return ret;
}
int main()
{
    scanf("%d",&n);get_table();
    int l=1,r;
    while (l<=n)
    {
        r=n/(n/l);
        int top=n/l,l1=1,r1,ret=0;
        while (l1<=top) {r1=top/(top/l1);ret=(ret+(long long)top/l1*(r1-l1+1)%mod)%mod;l1=r1+1;}
        ret=(long long)ret*ret%mod;
        ans=(ans+(long long)(ask(r)-ask(l-1)+mod)%mod*ret%mod)%mod;
        l=r+1;
    }
    printf("%d\n",ans);
    return 0;
}