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还没考虑啊……我们离线,从小到大来算,用树状数组来维护求和
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }