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\n",ans[i]&0x7fffffff);
    return 0;
}

 

posted @ 2018-10-25 19:28  Star_Feel  阅读(188)  评论(0编辑  收藏  举报