您的位置:首页 > 其它

[51nod1203]JZPLCM

2017-01-06 20:24 302 查看

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必然出现pk−1,每一个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
;
int n,m,x,y,tot,l
,r
,h
,an
;
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]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: