zoukankan      html  css  js  c++  java
  • BZOJ3529: [Sdoi2014]数表

    【传送门:BZOJ3529


    简要题意:

      给出一张数表,数表上第i行第j列的格子上的数是所有同时整除i和j的自然数的和

      给出Q个询问,每个询问输入n,m,a,求出n*m的数表中格子上的数<=a的所有格子的和


    题解:

      设F(x)为x的约数和,设n<m

      实际上就是求$sum_{i=1}^{n}sum_{j=1}^{m}F(gcd(i,j))[F(gcd(i,j))<=a]$

      我们先把$F(gcd(i,j))<=a$这个限制条件省略

      那么原始就等于$sum_{i=1}^{n}sum_{j=1}^{m}F(gcd(i,j))$

      先枚举d=gcd(i,j),得到$$sum_{d=1}^{n}F(d)*sum_{i=1}^{frac{n}{d}}sum_{j=1}{frac{m}{d}}[gcd(i,j)==1]$$

      设G(x)为gcd(i,j)%x==0的数对数,f(x)为gcd(i,j)==x的数对数,显然$G(n)=sum_{n|d}f(d)$,根据反演公式得到$f(n)=sum_{n|d}mu(frac{d}{n})G(d)$

      因为$G(x)=frac{n}{x}*frac{m}{x}$,所以原式等于$$sum_{d=1}^{n}F(d)*sum_{d|i}mu(frac{i}{d})*frac{n}{i}*frac{m}{i}$$

      先枚举i,得到$$sum_{i=1}^{n}frac{n}{i}*frac{m}{i}*sum_{d|i}F(d)*mu(frac{i}{d})$$

      然后呢,再加入限制条件,因为实际上只有当F(i)<=a时,i才能取,那么我们先预处理F,然后让F的值从小到大排序,将询问按照a的值从小到大排序

      设$D(d)=sum_{i|d}F(i)*mu(frac{d}{i})$,对于一个新进来的i,如果F(i)<=a,则将所有i|d的D(d)全部加上$F(i)*mu(frac{d}{i})$,可以用树状数组维护前缀和就行了

      PS:不要用long long,不要%,%太慢了会超时,直接溢出,然后将答案&0x7fffffff


    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int miu[110000],prime[110000],v[110000];
    struct F
    {
        int t,d;
    }f[110000];
    bool cmpf(F n1,F n2){return n1.d<n2.d;}
    void pre(int n)
    {
        miu[1]=1;int m=0;
        for(int i=2;i<=n;i++)
        {
            if(v[i]==0)
            {
                v[i]=i;
                prime[++m]=i;
                miu[i]=-1;
            }
            for(int j=1;j<=m;j++)
            {
                if(prime[j]>v[i]||prime[j]>n/i) break;
                v[i*prime[j]]=prime[j];
                if(i%prime[j]==0) miu[i*prime[j]]=0;
                else miu[i*prime[j]]=-miu[i];
            }
        }
        for(int i=1;i<=n;i++)
        {
            f[i].t=i;
            for(int j=i;j<=n;j+=i) f[j].d+=i;
        }
    }
    struct query
    {
        int n,m,a,id;
    }q[21000];
    bool cmp(query n1,query n2){return n1.a<n2.a;}
    int ans[21000];
    int a[110000];
    int mn;
    int lowbit(int x){return x&-x;}
    void change(int x,int d)
    {
        while(x<=mn)
        {
            a[x]+=d;
            x+=lowbit(x);
        }
    }
    int getsum(int x)
    {
        int ans=0;
        while(x!=0)
        {
            ans+=a[x];
            x-=lowbit(x);
        }
        return ans;
    }
    void getd(int x)
    {
        for(int i=1,j;i<=q[x].n;i=j+1)
        {
            j=min(q[x].n/(q[x].n/i),q[x].m/(q[x].m/i));
            ans[q[x].id]+=(q[x].n/i)*(q[x].m/i)*(getsum(j)-getsum(i-1));
        }
    }
    int main()
    {
        pre(100000);
        int Q;
        scanf("%d",&Q);mn=0;
        for(int i=1;i<=Q;i++)
        {
            scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].a);
            q[i].id=i;if(q[i].n>q[i].m) swap(q[i].n,q[i].m);
            mn=max(q[i].n,mn);
        }
        sort(q+1,q+Q+1,cmp);sort(f+1,f+mn+1,cmpf);
        memset(a,0,sizeof(a));
        for(int i=1,j=0;i<=Q;i++)
        {
            while(j+1<=mn&&f[j+1].d<=q[i].a)
            {
                j++;
                for(int k=f[j].t;k<=mn;k+=f[j].t) change(k,f[j].d*miu[k/f[j].t]);
            }
            getd(i);
        }
        for(int i=1;i<=Q;i++) printf("%d
    ",ans[i]&0x7fffffff);
        return 0;
    }

     

  • 相关阅读:
    【SQL】在含有GROUP BY的SELECT语句中如何显示COUNT()为0的结果
    【SQL】SQL分页查询总结
    开篇
    Android Native Crash 排查思路
    jmeter+ant+jenkins接口自动化测试框架
    为何推荐使用线程池而不是显式创建线程原因之一—至少让线程有范围和限制
    quartz 中的线程池
    select in 查询结果无顺序及解决办法
    Druid 数据库连接池如何根据url加载Driver
    java 线程池参数
  • 原文地址:https://www.cnblogs.com/Never-mind/p/9850890.html
Copyright © 2011-2022 走看看