码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ4635 : 数论小测验

时间:2016-06-29 06:33:41      阅读:211      评论:0      收藏:0      [点我收藏+]

标签:

第一问:

设$a[i]$表示使用$[1,i]$的数字$n$次形成的数组里有多少个$\gcd=1$。

考虑容斥,则$a[i]=i^n-\sum_{j=2}^i a[\lfloor\frac{i}{j}\rfloor]$,可以分$\sqrt{i}$段算出。

$ans=\sum_{i=l}^r a[\lfloor\frac{m}{i}\rfloor]$,也可以分$\sqrt{m}$段算出。

注意到只有$\sqrt{m}$个$a[i]$被使用到,所以只计算它们即可。

时间复杂度$O(m)$。

 

第二问:

设$s[i][j]$表示$1$到$j$里有多少数字与$i$互质,可以通过$O(m^2)$的预处理求出。

对于一个询问,枚举所有$k$,计算$k|lcm$的答案。

将$k$分解质因数,则使用的$n$个数字相应质因子的指数不能比它小。

考虑容斥,转化成求有多少数字对应指数都比它小。

设$k$的质因子乘积为$mul$,则方案数为$(\sum_{d|\frac{k}{mul}}s[mul][\lfloor\frac{m}{d}\rfloor])^n$。

时间复杂度$O(m\log m)$。

 

#include<cstdio>
const int N=1001,M=7100,P=1000000007;
int T,type,t,n,m,l,r,i,j;
int f[N][N],s[N][N],G[N],V[M],NXT[M],mul[N],g[N],v[M],nxt[M],ed;
int p[N],a[10000001],b[N],ans;
inline void up(int&a,int b){a+=b;while(a>=P)a-=P;}
inline int pow(int a,int b){int t=1;for(;b;b>>=1,a=1LL*a*a%P)if(b&1)t=1LL*t*a%P;return t;}
inline void init(int x,int n){
  int t=0,i,j;
  for(i=2;i<=x;i=j+1)j=x/(x/i),up(t,1LL*(j-i+1)*a[x/i]%P);
  a[x]=(pow(x,n)+P-t)%P;
}
void dfs(int x,int y,int z){
  if(x==t){
    if(z==1)return;
    mul[y>0?y:-y]=z;
    v[++ed]=y;nxt[ed]=g[i];g[i]=ed;
    return;
  }
  dfs(x+1,y,z),dfs(x+1,-y*a[x],z*b[x]);
}
inline void cal(int x){
  up(ans,p[m]);
  for(int i=g[x];i;i=nxt[i]){
    if(v[i]>0)up(ans,p[a[v[i]]]);
    else up(ans,P-p[a[-v[i]]]);
  }
}
int main(){
  scanf("%d%d",&T,&type);
  if(type==1){
    a[1]=1;
    while(T--){
      scanf("%d%d%d%d",&n,&m,&l,&r);ans=0;
      for(i=1;i<=m;i=m/(m/i)+1)v[++t]=m/i;
      while(t)init(v[t--],n);
      for(i=l;i<=r;i=j+1){
        j=m/(m/i);
        if(j>r)j=r;
        up(ans,1LL*(j-i+1)*a[m/i]%P);
      }
      printf("%d\n",ans);
    }
  }else{
    for(i=0;i<N;i++)f[0][i]=f[i][0]=f[i][i]=i;
    for(i=2;i<N;i++)for(j=1;j<i;j++)f[i][j]=f[j][i]=f[i-j][j];
    for(i=1;i<N;i++)for(j=1;j<N;j++)s[i][j]=s[i][j-1]+(f[i][j]==1);
    for(i=1;i<N;i++)for(j=i;j<N;j+=i)V[++ed]=i,NXT[ed]=G[j],G[j]=ed;
    for(ed=0,i=2;i<N;i++){
      for(n=i,t=0,j=2;j<=n;j++)if(n%j==0){
        for(m=1;n%j==0;n/=j,m*=j);
        a[t]=m,b[t++]=j;
      }
      dfs(0,1,1);
    }
    while(T--){
      scanf("%d%d%d%d",&n,&m,&l,&r);ans=0;
      for(i=1;i<=m;i++)p[i]=pow(i,n);
      for(i=2;i<=m;i++)for(a[i]=0,j=G[i/mul[i]];j;j=NXT[j])a[i]+=s[i][m/V[j]];
      while(l<=r)cal(l++);
      printf("%d\n",ans);
    }
  }
  return 0;
}

  

BZOJ4635 : 数论小测验

标签:

原文地址:http://www.cnblogs.com/clrs97/p/5625508.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!