zoukankan      html  css  js  c++  java
  • [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))

    [egin{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}^{lfloorfrac{n}{d} floor}sum_{j=1}^{lfloorfrac{m}{d} floor} 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} floor} mu(k) lfloorfrac{n}{kd} floor lfloorfrac{m}{kd} floor end{align} ]

    (T=kd)

    [egin{align} (*)&=sum_{T=1}^c lfloorfrac{n}{d} floor lfloorfrac{m}{d} floor 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(qsqrt{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
    ",ans[i]&((1ll<<31)-1));
    }
    
  • 相关阅读:
    企业微信开发基本步骤
    简单的企业微信开发 前后端
    真分页
    企业微信“三次握手”
    Android项目的图标
    Android项目的目录结构
    Android系统提供了哪些东西,供我们可以开发出优秀的应用程序
    Android中的四层架构,五块区域
    MySQL中的concat函数
    Activity生命周期
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/14586320.html
Copyright © 2011-2022 走看看