【51nod 1203】【JZPLCM】【莫队算法】

题目大意

给出一列数,求一段区间的lcm。

解题思路

离线询问,对小于sqrt(n)的质因子暴力求出,rmq解决询问。这样之后就只最多剩下一个质因子,可以使用莫队用桶维护。

code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define Min(a,b) ((a<b)?a:b)
#define Max(a,b) ((a>b)?a:b)
#define Fo(i,j,k) for(int i=j;i<=k;i++)
#define Fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const Mxn=5*1e4;
int N,Q,Size,A[Mxn+9],Ha[Mxn+9],Cnt[Mxn+9],F[Mxn+9][17],Two[17],Ans[Mxn+9],Mo=1e9+7;
struct Rec{int L,R,P;};
Rec B[Mxn+9];
bool Cmp(Rec X,Rec Y){
    return ((X.L-1)/Size<(Y.L-1)/Size)||(((X.L-1)/Size==(Y.L-1)/Size)&&(X.R<Y.R));
}
int Pow(LL X,int Y){
    LL Z=1;
    while(Y){
        if(Y&1)Z=Z*X%Mo;
        X=X*X%Mo;
        Y>>=1;
    }
    return Z;
}
int Calc(int L,int R){
    int X=log(R-L+1)/log(2);
    return Max(F[L][X],F[R-Two[X]+1][X]);
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d%d",&N,&Q);int Mx=0,Mx2=log(N)/log(2),Tmp,Ok;Size=sqrt(N);
    Two[0]=1;Fo(i,1,Mx2)Two[i]=Two[i-1]<<1;
    Fo(i,1,N)scanf("%d",&A[i]),Mx=Max(Mx,A[i]);Mx=sqrt(Mx);
    Fo(i,1,Q)scanf("%d%d",&B[i].L,&B[i].R),B[i].P=i,Ans[i]=1;
    Fo(i,2,Mx){
        Tmp=sqrt(i);Ok=1;
        Fo(j,2,Tmp)if(i%j==0){Ok=0;break;}
        if(Ok){
            Fo(j,1,N){
                F[j][0]=0;
                while(A[j]%i==0)F[j][0]++,A[j]/=i;
            }
            Fo(k,1,Mx2)Fd(j,N-Two[k-1],1)F[j][k]=Max(F[j][k-1],F[j+Two[k-1]][k-1]);
            Fo(j,1,Q)Ans[j]=1ll*Ans[j]*Pow(i,Calc(B[j].L,B[j].R))%Mo;
        }
    }
    sort(B+1,B+Q+1,Cmp);
    int Next;LL Now;
    Fo(i,1,Q)if(((B[i].L-1)/Size!=(B[i-1].L-1)/Size)||(i==1)){
        if(B[i].P==14){
            int bb;
            bb++;
        }
        Now=1;Next=(B[i].L-1)/Size*Size+Size;
        Fo(j,1,Mxn)Cnt[j]=0;
        Fo(j,Next+1,B[i].R){
            if(!Cnt[A[j]])Now=(Now*A[j])%Mo;
            Cnt[A[j]]++;
        }
        LL Tmp=Now;
        Fo(j,B[i].L,Min(Next,B[i].R)){
            if(!Cnt[A[j]])Now=(Now*A[j])%Mo;
            Cnt[A[j]]++;
        }
        Fo(j,B[i].L,Min(Next,B[i].R))Cnt[A[j]]--;
        Ans[B[i].P]=1ll*Ans[B[i].P]*Now%Mo;Now=Tmp;
    }else{
        if(B[i].P==14){
            int bb;
            bb++;
        }
        Fo(j,Max(Next+1,B[i-1].R+1),B[i].R){
            if(!Cnt[A[j]])Now=(Now*A[j])%Mo;
            Cnt[A[j]]++;
        }
        LL Tmp=Now;
        Fo(j,B[i].L,Min(Next,B[i].R)){
            if(!Cnt[A[j]])Now=(Now*A[j])%Mo;
            Cnt[A[j]]++;
        }
        Fo(j,B[i].L,Min(Next,B[i].R))Cnt[A[j]]--;
        Ans[B[i].P]=1ll*Ans[B[i].P]*Now%Mo;Now=Tmp;
    }
    Fo(i,1,Q)printf("%d\n",Ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值