[51nod1203]JZPLCM

Description

给出n个数ai,m次询问,每次询问区间[l,r]的LCM。
答案对10^9+7取模。
n,m,ai<=50000

Solution

再次被推毒。
这道题做法挺多的说~
似乎许多dalao都是直接上莫队踩过去的~
栋爷的平衡规划
听说这道题是集训队原题,然后原题ai<=10^9?

首先我们考虑LCM是什么?就是所有质数在这个区间中出现的次数最大值的乘积。
发现找最大值很不吼,于是我们可以考虑用另一种方法。
我们把每个 pk 拆成 p1 , p2 , p3 pk
次数最多是什么意思?因为出现 pk 必然出现 pk1 ,每一个 pi 我们可以只选1个,然后选出多少个最高次数就是几。
那么也就是一堆数有特征值和权值,求一个区间所有特征值不同的数的权值乘积。
那么不就是直接上莫队就好了。
不过也可以不用,因为这道题不带修改,所以离线把所有询问排序,维护每一个特征值下一个相同的特征值的位置,从左往右扫过去,每到一个点解决所有以这个点为左端点的询问。
区间求乘积树状数组常数多小=w=

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int N=5*1e4+5,mo=1e9+7;
struct note{
    int l,r,id;
    friend bool operator < (note x, note y) {
        return x.l<y.l;
    }
}ask[N];
int n,m,x,y,tot,l[N],r[N],h[N],an[N];
int tr[N*15],next[N*15],a[N*15],b[N*15];
int mi(int x,int y) {
    int z=1;
    for(;y;y/=2,x=(ll)x*x%mo) if (y&1) z=(ll)z*x%mo;
    return z;
}
void add(int x,int y) {
    for(;x<=tot;x+=x&-x) tr[x]=(ll)tr[x]*y%mo;
}
int find(int x) {
    int ans=1;
    for(;x;x-=x&-x) ans=(ll)ans*tr[x]%mo;
    return ans;
}
int main() {
    scanf("%d%d",&n,&m);
    fo(j,1,n) {
        scanf("%d",&x);int t=sqrt(x);
        if (x==1) continue;l[j]=tot+1;
        fo(i,2,t) if (!(x%i)) {
            int k=i;
            while (!(x%i)) x/=i,a[++tot]=k,b[tot]=i,k*=i;
        }
        if (x>1) a[++tot]=b[tot]=x;
        r[j]=tot;
    }
    fo(i,1,tot) tr[i]=1;
    fo(i,1,tot) {
        if (h[a[i]]) next[h[a[i]]]=i;
        else add(i,b[i]);
        h[a[i]]=i;
    }
    fo(i,1,m) {
        scanf("%d%d",&ask[i].l,&ask[i].r);
        ask[i].l=l[ask[i].l];ask[i].r=r[ask[i].r];
        ask[i].id=i;
    }
    sort(ask+1,ask+m+1);int j=0;
    fo(i,1,tot) {
        while (ask[j+1].l==i) an[ask[++j].id]=find(ask[j].r);
        add(i,mi(b[i],mo-2));
        if (next[i]) add(next[i],b[next[i]]);
    }
    fo(i,1,m) printf("%d\n",an[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值