[SDOI2014]数表

Description

\(q\) 次询问,每次给出 \(n\)\(m\)\(a\),求

\[\sum_{i=1}^{n}\sum_{j=1}^m \sigma_1(gcd(i,j))[\sigma_1(gcd(i,j))\leq a] \]

Solution

按套路枚举 \(gcd(i,j)\),记 \(c=min(n,m)\)

\[\begin{align} (*)&=\sum_{d=1}^{c} \sigma_1(d)[\sigma_1(d)\leq a] \sum_{i=1}^{n}\sum_{j=1}^m [gcd(i,j)=d] \\ &=\sum_{d=1}^{c} \sigma_1(d)[\sigma_1(d)\leq a] \sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor} \sum_{k|i,k|j} \mu(k) \\ &=\sum_{d=1}^{c} \sigma_1(d)[\sigma_1(d)\leq a] \sum_{k=1}^{\lfloor \frac{n}{d} \rfloor} \mu(k) \lfloor\frac{n}{kd}\rfloor \lfloor\frac{m}{kd}\rfloor \end{align} \]

\(T=kd\)

\[\begin{align} (*)&=\sum_{T=1}^c \lfloor\frac{n}{d}\rfloor \lfloor\frac{m}{d}\rfloor \sum_{d|T} [\sigma_1(d)\leq a]\sigma_1(d)\mu(\frac{T}{d}) \end{align} \]

如果没有 \(a\) 的限制,那应该相当好做。外和可以整数分块,内和是积性函数,可以线性筛预处理,再做前缀和。但加了 \(a\) 的限制,内和是动态的,怎么维护?

询问间相互独立,所以能想到将询问按 \(a\) 升序离线下来。那么每次询问之前,只需要把 \(\sigma_1(d)\) 介于 \(a_{i-1}+1\)\(a_i\)\(d\) 加入到数组中。每个 \(d\) 只会被加一遍。每个 \(d\) 会影响 \(\frac{n}{d}\) 个内和,那么总修改次数就是调和级数,为 \(O(n \log n)\) 。而我们求和的时候是查询一个区间的内和,所以想到用树状数组直接维护动态前缀和。

总复杂度 \(O(q\sqrt{n}log_n+n \log^2 n)\)

#include<stdio.h>
#include<algorithm>
using namespace std;
#define M 20007
#define N 100007
#define ll unsigned int

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

struct Que{
    int n,m,a,pos;
    bool operator <(const Que &X) const{return a<X.a;}
}q[M];

struct Node{
    int seg,d;
    Node(int seg_=0,int d_=0):seg(seg_),d(d_){}
    bool operator <(const Node &X) const{return seg<X.seg;}
}a[N];

int T,p[N],cnt=0,d[N];
ll ans[M],mu[N],seg[N],c[N];
bool mk[N];

inline int lowbit(int x){return -x&x;}
inline void add(int x,ll v){while(x<N)c[x]+=v,x+=lowbit(x);}
inline ll query(int x){ll ret=0;while(x)ret+=c[x],x-=lowbit(x);return ret;}

inline int min(int x,int y){return x<y? x:y;}

int main(){
    T=read();
    for(int i=1;i<=T;i++)
        q[i].n=read(),q[i].m=read(),q[i].a=read(),q[i].pos=i;
    sort(q+1,q+1+T);
    seg[1]=mu[1]=1; a[1]=Node(1,1);
    for(int i=2;i<N;i++){
        if(!mk[i])
            d[i]=p[++cnt]=i,seg[i]=i+1,mu[i]=-1;
        for(int j=1;j<=cnt&&p[j]*i<N;j++){
            mk[p[j]*i]=1;
            if(i%p[j]){
                mu[i*p[j]]=-mu[i];
                d[i*p[j]]=p[j];
                seg[i*p[j]]=seg[i]*seg[p[j]];
            }else{
                d[i*p[j]]=d[i]*p[j];
                if(d[i*p[j]]!=i*p[j])
                    seg[i*p[j]]=seg[i/d[i]]*seg[d[i*p[j]]];
                else seg[i*p[j]]=seg[i]*p[j]+1;
                break;
            }
        }
        a[i]=Node(seg[i],i);
    }
    sort(a+1,a+N); int pos=1;
    for(int i=1;i<=T;i++){
        while(pos<N&&a[pos].seg<=q[i].a){
            for(int j=1;j*a[pos].d<N;j++)
                add(j*a[pos].d,a[pos].seg*mu[j]);
            pos++;
        }
        int n=q[i].n,m=q[i].m;
        ll ret=0; int rg=min(n,m);
        for(int l=1,r;l<=rg;l=r+1){
            r=min(n/(n/l),m/(m/l));
            ret+=(n/r)*(m/r)*(query(r)-query(l-1));
        }
        ans[q[i].pos]=ret;
    }
    for(int i=1;i<=T;i++) printf("%d\n",ans[i]&((1ll<<31)-1));
}
posted @ 2021-03-27 17:17  Kreap  阅读(59)  评论(0编辑  收藏  举报