解题报告:BZOJ_3529:数表 莫比乌斯反演+离线处理

题目链接

题意:

一张n*m的表格,( i , j )的格子上的数为gcd( i , j ) 的约数和,询问所有格子的不超过a的数之和,T组数据。

1<=n,m<=1e5,|a|<=1e9,T<=2e4


思路:

这题很多细节不是很容易想到。。

为了方便表述,定义:

F(x):x的约数和,可以预处理得到

G(x):满足题目的gcd==x的组数


我们先分析g[i]:



得到:

这个看上去已经很快了。。但是还是会TLE,继续优化:


到这里发现后面的部分是个狄利克雷卷积,可以利用树状数组维护,这样就可以在的时间内完成一次查询

那么这部分的更新的总复杂度为,属于可以接受的时间复杂度

为了方便统计答案的时候不考虑大于a的格子,考虑离线处理

将所有询问以a进行排序,每次询问将所有小于a的f(x)的所有倍数插入到树状数组中。

那么总的复杂度就为:


代码:

#include<bits/stdc++.h>

#define lowbit(x) (x&(-x))

const int N = 1e5+10;
const int mod = (1LL<<31)-1;

using namespace std;

bool Np[N];
int mu[N],A[N],valE[N],inde[N],ID[N],ans[N];
vector<int>pr;

inline void add(int x,int val){
   while(x<N){
      A[x]+=val;
      A[x]&=mod;
      x+=lowbit(x);
   }
}

inline int query(int x){
   int res = 0;
   while(x){
      res = (res+A[x])&mod;
      x -= lowbit(x);
   }return res;
}

struct que{
   int n,m,a;
}Q[N];

bool cmp1(const int& a,const int& b){
   return valE[a]<valE[b];
}

bool cmp2(const int& a,const int& b){
   return Q[a].a<Q[b].a;
}

void init(){
   mu[1] = 1;
   for(int i=2;i<N;i++){
      if(!Np[i]){
         pr.push_back(i);
         mu[i] = -1;
      }for(int j=0,k=pr[0]*i;k<N;k=pr[++j]*i){
         Np[k] = true;
         if(i%pr[j]==0){
            mu[k] = 0;
            break;
         }mu[k] = -mu[i];
      }mu[i] += mu[i-1];
   }for(int i=1;i<N;i++){
      ID[i] = i;
      for(int j=i;j<N;j+=i){
         valE[j] += i;
      }
   }sort(ID+1,ID+N,cmp1);
}


int work(int n,int m){
   int res = 0 ;
   for(int i=1,last;i<=n;i=last+1){
      int nn = n/i , mm = m/i;
      last = min ( n / nn , m / mm );
      res += (query(last)-query(i-1) )*nn*mm;
      res &= mod;
   }return (res&mod);
}


int main()
{
   //freopen("data.in","r",stdin);
   //freopen("my_ans.out","w",stdout);
   init();
   int q;
   scanf("%d",&q);
   for(int i=1;i<=q;i++){
      inde[i] = i;
      scanf("%d%d%d",&Q[i].n,&Q[i].m,&Q[i].a);
      if(Q[i].n>Q[i].m)swap(Q[i].n,Q[i].m);
   }sort(inde+1,inde+q+1,cmp2);
   for(int qq=1,pos=1;qq<=q;qq++){
      int p = inde[qq];
      while(pos<N&&valE[ID[pos]]<=Q[p].a){
         for(int i=1,j=ID[pos]*i;j<N;j+=ID[pos],i++)if(mu[i]-mu[i-1]){
            add(j,valE[ID[pos]]*(mu[i]-mu[i-1]));
         }pos++;
      }ans[p] = work(Q[p].n,Q[p].m);
   }for(int i=1;i<=q;i++){
      printf("%d\n",ans[i]);
   }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章