BZOJ3529 SDOI2014 数表 莫比乌斯反演+树状数组

题意:T组询问,每组询问给出N,M,a,有一张N×m的数表,其第i行第j列(1≤i≤N,1≤j≤M)的数值为能同时整除i和j的所有自然数之和。给定a,计算数表中不大于a的数之和。

题解:

首先我们预处理出来所有数的约数和,也就是F函数,定义g(i)=1到N和1到M中gcd含有i这个因子的组数,这玩意等于啥我就不多提了,不知道的话建议去做一下2301。

接下来就全是套路了——枚举因数,然后求和F(i)*g(i),莫比乌斯反演,然后函数分块。

等等,貌似a还没考虑啊……我们离线,从小到大来算,用树状数组来维护求和

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define lowbit(x) (x&(-x))

const int MAXN=100000+2;
const int MAXQ=20000+2;
struct STR{
    int N,M,V,ID;
}f[MAXN],q[MAXQ];
int T,mu[MAXN],prime[MAXN],n,ans[MAXQ],a[MAXN];
bool flag[MAXN];

bool cmp(STR a,STR b){ return a.V<b.V;}

void Moebius(int N){
    int cnt=0;
    mu[1]=1;
    for(int i=2;i<=N;i++){
        if(!flag[i]) prime[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt && i*prime[j]<=N;j++){
            flag[i*prime[j]]=1;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j+=i) f[j].V+=i;
        f[i].N=i;
    }
}

void Insert(int p,int x){
    while(p<=n) a[p]+=x,p+=lowbit(p);
}

int Query(int x){
    int ret=0;
    while(x>0) ret+=a[x],x-=lowbit(x);
    return ret;
}

int main(){
    cin >> T;
    for(int i=1;i<=T;i++){
        scanf("%d %d %d",&q[i].N,&q[i].M,&q[i].V);
        n=max(n,max(q[i].N,q[i].M)),q[i].ID=i;
    }

    Moebius(n);

    sort(f+1,f+n+1,cmp),sort(q+1,q+T+1,cmp);

    q[0].V=0;
    for(int i=1,j=0;i<=T;i++){
        while(j<n && f[j+1].V<=q[i].V){
            j++;
            for(int k=f[j].N;k<=n;k+=f[j].N) Insert(k,f[j].V*mu[k/f[j].N]);
        }

        for(int k=1,next;k<=q[i].N && k<=q[i].M;k=next+1){
            next=min(q[i].N/(q[i].N/k),q[i].M/(q[i].M/k));
            ans[q[i].ID]+=(Query(next)-Query(k-1))*(q[i].N/k)*(q[i].M/k);
        }
    }

    for(int i=1;i<=T;i++) cout << (ans[i]&0x7fffffff) << endl;

    return 0;
}
View Code

 

posted @ 2017-02-27 00:02  WDZRMPCBIT  阅读(167)  评论(0编辑  收藏  举报