[51nod1203]JZPLCM

来源:互联网 发布:淘宝卖家版下载 编辑:程序博客网 时间:2024/06/08 00:11

Description

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

Solution

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

首先我们考虑LCM是什么?就是所有质数在这个区间中出现的次数最大值的乘积。
发现找最大值很不吼,于是我们可以考虑用另一种方法。
我们把每个pk拆成p1,p2,p3pk
次数最多是什么意思?因为出现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
原创粉丝点击