【JZOJ4161】于神之怒 莫比乌斯反演

任务

这里写图片描述
这里写图片描述
答案mod 1e9+7.

解法

容易写出反演:

Ans=T=1nTki=1nTniTmiTμ(i)

nTi=1niTmiTμ(i)这个因式显然是经典的分块处理
同时我们还发现,T满足nTmT相等时,这个因式是相等的。
所以我们还可以对T进行分块
总的时间复杂度就是O(n)


另外的Trick:
当我们在对T进行分块之前,
我们还需预处理出Tk的前缀和。
由于逐个预处理Tk会超时,所以可以考虑利用线性筛法预处理Tk

代码

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<math.h>
#include<string.h>
#define ll long long
using namespace std;
const char* fin="ex4161.in";
const char* fout="ex4161.out";
const ll inf=0x7fffffff;
const ll maxn=5000007,mo=1e9+7;
ll n,m,N,i,j,k,ans,t;
ll mu[maxn],p[maxn],s[maxn];
bool bz[maxn];
ll qpower(ll a,ll b){
    ll c=1;
    while (b){
        if (b&1) c=c*a%mo;
        a=a*a%mo;
        b>>=1;
    }
    return c;
}
int main(){
    scanf("%d%d%d",&n,&m,&N);
    if (n>m) swap(n,m);
    mu[1]=1;
    s[1]=1;
    for (i=2;i<maxn;i++){
        if (!bz[i]){
            mu[i]=-1;
            s[i]=qpower(i,N);
            p[++p[0]]=i;
        }
        for (j=1;j<=p[0];j++){
            k=i*p[j];
            if (k>=maxn) break;
            bz[k]=true;
            s[k]=s[i]*s[p[j]]%mo;
            if (i%p[j]==0){
                mu[k]=0;
                break;
            }else mu[k]=-mu[i];
        }
    }
    for (i=1;i<maxn;i++){
        mu[i]+=mu[i-1];
        s[i]=(s[i-1]+s[i])%mo;
    }
    ll cnt=0;
    for (ll T=1;T<=n;){
        t=min(n/(n/T),m/(m/T));
        ll tmp=0,tmd=n/T,tmb=m/T;
        for (i=1;i<=tmd;){
            cnt++;
            j=min(tmd/(n/(i*T)),tmb/(m/(i*T)));
            tmp=(tmp+(tmd/i)*(tmb/i)*(mu[j]-mu[i-1]))%mo;
            i=j+1;
        }
        ans=(ans+tmp*(s[t]-s[T-1]))%mo;
        T=t+1;
    }
    ans=(ans%mo+mo)%mo;
    printf("%lld",ans);
    return 0;
}

Warning

注意卡常,先预算出nTmT,可大幅降低常数。

posted @ 2017-03-14 15:28  hiweibolu  阅读(143)  评论(0编辑  收藏  举报