bzoj3529 [ SDOI2014 ] -- 莫比乌斯反演+分块

题目大意:

i=1nj=1mσ(gcd(i,j))[σ(gcd(i,j))a]

其中 σ(x) 表示 x 的约数和。
先假设没有a的限制,且 nm
那么就是求

i=1nj=1mσ(gcd(i,j))

i=1nj=1md=1nσ(d)[d=gcd(i,j)]

d=1nσ(d)i=1ndj=1md[gcd(i,j)=1]

d=1nσ(d)i=1ndj=1mdt|i,t|jμ(t)

d=1nσ(d)t=1ndμ(t)ndtmdt

T=dt

T=1nnTmTd|Tσ(d)μ(Td)

f(x)=d|xσ(d)μ(xd)

T=1nnTmTf(T)

那么 O(nlogn) 求出 f ,就能O(qn)求出答案了。
然后考虑有 a 的限制,那么

f(x)=d|xσ(d)μ(xd)[σ(d)a]

离线,将询问按照 a 从小到大排序,每次用ai1+1σ(d)ai σ(d) 更新 f ,用树状数组维护前缀和就可以了。
时间复杂度O(nlog2n+qnlogn)

代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define M 2147483648
#define N 100010
struct Qu{
    int x,y,z,f;
}q[20010];
struct Node{
    int w,f;
}a[N];
ll c[N],f[N],Ans[N];
int Q,i,j,k,n,m,p[N],Top,mu[N],Last;
bool b[N];
inline bool Cmp2(Node a,Node b){
    return a.w<b.w;
}
inline bool Cmp(Qu a,Qu b){
    return a.z<b.z;
}
inline void Init(){
    mu[1]=1;
    for(i=2;i<N;i++){
        if(!b[i]){
            p[++Top]=i;
            mu[i]=-1;
        }
        for(j=1;j<=Top&&p[j]*i<N;j++){
            b[p[j]*i]=1;
            if(!(i%p[j])){mu[p[j]*i]=0;break;}
            mu[p[j]*i]=-mu[i];
        }
    }
    for(i=1;i<N;i++)
    for(j=1;j*i<N;j++)
    f[i*j]+=i;
    for(i=1;i<N;i++)a[i].w=f[i],a[i].f=i;
    sort(a+1,a+N,Cmp2);
}
inline void Update(int x,ll y){
    for(;x<N;x+=x&-x)c[x]=(c[x]+y)%M;
}
inline ll Query(int x){
    ll Ans=0;
    for(;x;x-=x&-x)Ans+=c[x];
    return Ans;
}
inline void Update1(int x){
    for(int i=1;i*x<N;i++)Update(i*x,f[x]*mu[i]);
}
inline int Min(int x,int y){
    return x<y?x:y;
}
inline ll GetAns(int n,int m){
    ll Ans=0;
    for(int i=1;i<=n;){
        int j=Min(n/(n/i),m/(m/i));
        Ans=(Ans+(Query(j)-Query(i-1))*(n/i)%M*(m/i)%M)%M;
        i=j+1;
    }
    return Ans;
}
int main(){
    scanf("%d",&Q);
    for(i=1;i<=Q;i++){
        scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].z);
        if(q[i].x>q[i].y)swap(q[i].x,q[i].y);
        q[i].f=i;
    }
    Init();
    sort(q+1,q+Q+1,Cmp);
    for(i=1;i<=Q;i++){
        while(Last<N-1&&a[Last+1].w<=q[i].z)Update1(a[++Last].f);
        Ans[q[i].f]=GetAns(q[i].x,q[i].y);
    }
    for(i=1;i<=Q;i++)printf("%lld\n",(Ans[i]+M)%M);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值