3529: [Sdoi2014]数表|莫比乌斯反演|树状数组

以下式子均设 nm
先设

M(x)=d|xd

在不考虑a的限制下
Ans=i=1nj=1nM(gcd(i,j))

=i=1nM(i)sum(i)

sum(i) 表示 gcd(x,y)=i (x,y) 的个数
显然这个反演一下就可以得到
sum(i)=x=1niu(i)nixmix

Ans=i=1nM(i)x=1niu(i)nixmix

=T=1nnTmT(d|Tu(d)M(Td))

这样只需要线性筛搞出后面的这个 d|Tu(d)M(Td) 就可以 n 处理每次询问了
然后再回来考虑a的限制,只有 M(x)a 才会对答案有贡献,所以可以对询问按 a 的大小排序,每次都把小于当前的a的M(x)加入到树状数组中,这样离线处理每次询问
然后处理模数时中途直接全部 int 最后在 and 一下 int 的最大值就可以

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define ll long long
#define N 100005
using namespace std;
int sc()
{
    int i=0,f=1; char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i*f;
}
struct W{int n,m,p,a;}a[N];
int low[N],A[N],prime[N],tr[N];
int M[N],u[N],ans[N],pos[N];
void pre()
{
    int top=0;M[1]=u[1]=1;
    for(int i=2;i<N;i++)
    {
        if(!A[i])
        {
            u[low[i]=prime[++top]=i]=-1;
            M[i]=i+1;
        }
        for(int j=1;i*prime[j]<N;j++)
        {
            A[prime[j]*i]=1;
            if(i%prime[j]==0)
            {
                low[i*prime[j]]=low[i]*prime[j];
                if(low[i]==i)
                    M[i*prime[j]]=M[i]+i*prime[j];
                else M[i*prime[j]]=M[i/low[i]]*M[low[i]*prime[j]];
                break;
            }
            u[prime[j]*i]=-u[i];
            low[prime[j]*i]=prime[j];
            M[prime[j]*i]=M[i]*(prime[j]+1);
        }
    }
}   
bool cmp(W a,W b){return a.a<b.a;}
bool Cmp(int a,int b){return M[a]<M[b];}
void change(int x,int f)
{
    for(;x<N;x+=x&-x)tr[x]+=f;
}
int ask(int x)
{
    int ans=0;
    for(;x;x-=x&-x)ans+=tr[x];
    return ans;
}
int query(int n,int m)
{
    if(n>m)swap(n,m);
    int ans=0;
    for(int i=1,last;i<=n;i=last+1)
    {
        last=min(n/(n/i),m/(m/i));
        ans+=(n/i)*(m/i)*(ask(last)-ask(i-1));
    }
    return ans;
}
int main()
{
    pre();
    int W=sc();
    for(int i=1;i<=W;i++)
        a[i].n=sc(),a[i].m=sc(),a[i].a=sc(),a[i].p=i;
    sort(a+1,a+W+1,cmp);
    for(int i=1;i<N;i++)pos[i]=i;
    sort(pos+1,pos+N,Cmp);
    int now=1;
    for(int i=1;i<=W;i++)
    {
        while(now<N&&M[pos[now]]<=a[i].a)
        {
            int x=pos[now++];
            for(int j=x;j<N;j+=x)
                change(j,M[x]*u[j/x]);
        }
        ans[a[i].p]=query(a[i].m,a[i].n);
    }
    for(int i=1;i<=W;i++)
        printf("%d\n",ans[i]&0x7FFFFFFF);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值