解題報告: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]);
   }
}


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