BZOJ2988 : DIVISORS

令$NX=PQ$,其中$N<P\leq B$,则$1\leq Q<X$,对于每个满足条件的$N$,在最大的$Q$处统计它的贡献。

枚举最大的$Q$,那么$NX$要是$Q$和$X$的倍数,且不是$Q+1,Q+2,...,X-1$的倍数。

考虑容斥原理,指数级枚举$NX$一定是$Q+1$到$X-1$这些数中哪些数的倍数,剩下的不管,则对答案的贡献为$(-1)^{打破的限制数量}\lfloor\frac{BQ}{lcm}\rfloor$。

注意到$1..60$在$6\times 10^{13}$范围内的$lcm$数量不多(只有大约一百多万个),将容斥中的指数级枚举换成DP:

设$f[i][j]$表示考虑了$i$到$X-1$,被打破的限制的$lcm=j$的方案数乘以容斥系数之和,然后直接转移即可。

注意精细地实现做到$O(1)$转移,比如通过质因数分解来去掉求$lcm$的$O(\log X)$复杂度。

 

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=65,M=219,K=8197;
int Case,X,S,i,j,k,x,y,z;ll B,lim,ans;
int id[6][4][3][3],cnt,g[M],va[M],p2[9],p3[9],p5[9],p7[9];
int p[N],tot,all;
ll f[M][K],vb[K],_vb[K];
int q[K],at[K],to[K],en[N][M],r;
inline bool isprime(int x){
  for(int i=2;i<x;i++)if(x%i==0)return 0;
  return 1;
}
inline int get(int x,int y){
  int t=0;
  while(x%y==0)x/=y,t++;
  return t;
}
inline int mask(int x){
  int t=0;
  for(int i=0;i<tot;i++)if(x%p[i]==0)t|=1<<i;
  return t;
}
inline void gen(int o){
  int t=0;
  for(int i=0;i<6;i++)for(int j=0;j<4;j++)for(int x=0;x<3;x++)for(int y=0;y<3;y++)g[t++]=id[max(i,get(o,2))][max(j,get(o,3))][max(x,get(o,5))][max(y,get(o,7))];
}
inline bool cmp(int x,int y){return vb[x]<vb[y];}
int main(){
  for(p2[0]=p3[0]=p5[0]=p7[0]=i=1;i<6;i++){
    p2[i]=p2[i-1]*2;
    p3[i]=p3[i-1]*3;
    p5[i]=p5[i-1]*5;
    p7[i]=p7[i-1]*7;
  }
  for(i=0;i<6;i++)for(j=0;j<4;j++)for(x=0;x<3;x++)for(y=0;y<3;y++){
    va[cnt]=p2[i]*p3[j]*p5[x]*p7[y];
    id[i][j][x][y]=cnt++;
  }
  scanf("%d",&Case);
  while(Case--){
    scanf("%lld%d",&B,&X);
    tot=0;
    lim=B*X;
    for(i=X;i>=11;i--)if(isprime(i))p[tot++]=i;
    for(S=0;S<1<<tot;S++){
      ll t=1;
      for(i=0;i<tot;i++)if(S>>i&1){
        t*=p[i];
        if(t>lim)break;
      }
      vb[S]=min(t,lim+1);
    }
    all=(1<<tot)-1;
    for(i=0;i<=all;i++)q[i]=i;
    sort(q,q+all+1,cmp);
    for(i=0;i<=all;i++)at[q[i]]=i,_vb[i]=vb[q[i]];
    for(j=0;j<cnt;j++)en[X+1][j]=all;
    for(i=X;~i;i--){
      lim=B*i;
      for(j=0;j<cnt;j++){
        k=en[i+1][j];
        while(~k){
          if(va[j]>lim/_vb[k])k--;
          else break;
        }
        en[i][j]=k;
      }
    }
    ans=0;
    S=mask(X);
    for(i=0;i<cnt;i++){
      r=en[X][i];
      for(j=0;j<=r;j++)f[i][j]=0;
    }
    gen(X);
    f[g[0]][at[S]]=1;
    for(i=X-1;i;i--){
      lim=B*i;
      S=mask(i);
      gen(i);
      for(k=0;k<=all;k++)to[k]=at[q[k]|S];
      for(j=cnt-1;~j;j--){
        int v=va[j],nj=g[j],r=en[i][j];
        for(k=r;~k;k--)if(f[j][k]){
          ll A=v*_vb[k],W=f[j][k];
          int nk=to[k];
          A=va[nj]*_vb[nk];
          ans+=lim/A*W;
          if(A<=lim-B)f[nj][nk]-=W;
        }
      }
    }
    printf("%lld\n",ans);
  }
  return 0;
}

  

posted @ 2020-01-14 02:52  Claris  阅读(289)  评论(0编辑  收藏  举报