zoukankan      html  css  js  c++  java
  • [SDOI2014]数表

    [SDOI2014]数表

    在一张(n imes m)数表中,第i行第j列的数字为即能整除i又能整除j的自然数之和,Q组询问,询问这张数表中不大于a的数之和,1 < =n.m < =10^5 , 1 < =Q < =2*10^4。

    转换为约数计数问题,不难有

    [ans=sum_{i=1}^nsum_{j=1}^msigma(gcd(i,j)) ]

    因为限制数表中的数不大于a,于是我们得设的式子中要有(sigma(gcd(i,j))),后面推式也要保留其,于是设(nleq m),有

    [ans=sum_{d=1}^nsigma(d)sum_{i=1}^nsum_{j=1}^m(gcd(i,j)==d) ]

    [f(d)=sum_{i=1}^nsum_{j=1}^m(gcd(i,j)==d) ]

    [F(d)=[n/d][m/d] ]

    由Mobius反演定理有

    [f(d)=sum_{d|x}[n/x][m/x]mu(x/d) ]

    代入有

    [ans=sum_{d=1}^nsigma(d)sum_{d|x}[n/x][m/x]mu(x/d)= ]

    [sum_{x=1}^n[n/x][m/x]sum_{d|x}sigma(d)mu(x/d) ]

    注意到(sigma)为积性函数,我们可以线性递推,而后式显然可以维护,前式又可以整除分块,于是如果没有a的限制,我们已经可以做了,
    而此时能贡献的只有(sigma(d)leq a),维护的东西要求小于某个数,而询问又可以离线,于是考虑给a排序,给(sigma)排序,按照a的增加,慢慢地加入我们需要维护的后式的前缀和,所以我们需要支持快速修改,而树状数组就是一个不错的选择,所以不难得知时间复杂度应为(O(nlog(n)+Qsqrt{n}))

    参考代码:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #define il inline
    #define ri register
    #define ll long long
    #define sxr 100000
    #define yyb 2147483647
    #define swap(x,y) x^=y^=x^=y
    using namespace std;
    struct query{
        int n,m,a,id;
        il bool operator<(const query&x){
            return a<x.a;
        }
        il void sort(){
            if(n>m)swap(n,m);
        }
    }q[20001];
    template<class f1,class f2,f2 key>
    struct lowbit{
        f1 s[key+1];
        il void add(int p,int x){
            while(p<=key)
                s[p]+=x,p+=(-p)&p;
        }
        il f1 sum(int p){
            f1 ans(0);
            while(p)ans+=s[p],p-=(-p)&p;
            return ans;
        }
    };
    struct ysh{
        int id,dat;
        il bool operator<(ysh&x){
            return dat<x.dat;
        }
    }ds[sxr+1];
    bool check[sxr+1];
    int prime[sxr+1],pt,mb[sxr+1],
        df[sxr+1],ans[20001];
    lowbit<int,int,sxr+1>czf;
    il ll ask(int);
    il int min(int,int);
    il void prepare(int),read(int&);
    int main(){
        int lsy,i,j,k,l;prepare(sxr),read(lsy);
        for(i=1;i<=lsy;++i)
            read(q[i].n),read(q[i].m),read(q[i].a),
                q[i].id=i,q[i].sort();
        sort(q+1,q+lsy+1);
        for(i=1,j=1;i<=lsy;++i){
            while(ds[j].dat<=q[i].a&&j<=sxr){
                for(k=ds[j].id;k<=sxr;k+=ds[j].id)
                    czf.add(k,ds[j].dat*mb[k/ds[j].id]);
                ++j;
            }
            for(k=1;k<=q[i].n;k=l+1)
                l=min(q[i].n/(q[i].n/k),q[i].m/(q[i].m/k)),
                    ans[q[i].id]+=(q[i].n/k)*(q[i].m/k)*(czf.sum(l)-czf.sum(k-1));
        }for(i=1;i<=lsy;++i)printf("%d
    ",ans[i]&yyb);
        return 0;
    }
    il void read(int &x){
        x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    il int min(int a,int b){
        return a<b?a:b;
    }
    il void prepare(int n){
        int i,j;mb[1]|=ds[1].id|=ds[1].dat|=df[1]|=true;
        for(i=2;i<=n;++i){
            if(!check[i])mb[i]=-1,ds[i].dat=i+1,
                             df[i]=i+1,prime[++pt]=i;
            for(j=1;j<=pt&&prime[j]<=n/i;++j){
                check[i*prime[j]]|=true;
                if(!(i%prime[j])){
                    df[i*prime[j]]=df[i]*prime[j]+1;
                    ds[i*prime[j]].dat=ds[i].dat/df[i]*df[i*prime[j]];
                    break;
                }mb[i*prime[j]]=~mb[i]+1,
                     ds[i*prime[j]].dat=ds[i].dat*ds[prime[j]].dat,
                     df[i*prime[j]]=df[prime[j]];
            }ds[i].id=i;
        }sort(ds+1,ds+n+1);
    }
    
    
  • 相关阅读:
    滚动页面时DIV到达顶部时固定在顶部
    【Java学习笔记】拾遗
    【Java学习笔记】文件信息
    【Java学习笔记】使用BufferedReader类(流的读写)
    【Java学习笔记】可变参数
    【Java学习笔记】控制台读写
    【Java学习笔记】关于默认值
    【Java学习笔记】FileChannel的学习
    【JAVA学习笔记】静态导入
    【Java学习笔记】Java中关于tostring方法的误操作
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/10797023.html
Copyright © 2011-2022 走看看