CF1422F Boring Queries 题解

根号分治+ST表+主席树区间出现过的数的乘积

Statement

给定一个长度为 n 的序列 a 以及 q 次询问 。

每次询问包含 2 个整数 l,r ,你需要求出区间 [l,r] 的最小公倍数对 109+7 取模的结果。

询问强制在线 。

数据范围: 1n,q105,1ai2105,1x,y105

Solution

题目即是求 pici ,ci=max{ax,i} ,ax,i 表示 ax 的素数 pi 的次数

看到值域 105 ,考虑值域分块

105 的范围内只有 87 个素数,所以我们可以开 87 个 ST 表解决

然后我们把 a[i] 中小于 105 的素因子全部除掉,则 a[i] 只能是 1 或者单个 >105 的素数

问题就变成了算区间出现过的数的乘积,可以参照 HH 的项链 中的主席树做法

prei 表示值 i 前一次出现的位置,询问 l,r 即是:[prei<l]ai

那么我们搞一个主席树,root[k] 存储 preik 的信息

发现从 root[k1]root[k] 仅仅可能增加一个位置,所以确实可以主席树

特别的,root[0] 记录每个数第一次出现的位置的信息,方便询问

时间复杂度 O()

Code

注意到 ST 表空间有点卡,开成 char

#include<bits/stdc++.h>
#define ls lc[rt]
#define rs rc[rt]
#define mid ((l+r)>>1)
#define swap(x,y) x^=y^=x^=y
using namespace std;
typedef long long ll;
const int N = 2e5+5;
const int K = 450;
const int mod = 1e9+7;

int read(){
    int s=0,w=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
    return s*w;
}

int a[N],prime[100],lc[N*18],rc[N*18];
int pos[N<<1],b[N],Log[N],rot[N];
char f[90][N][18];
ll t[N*18],pw[100][21];
int n,q,cnt,siz;
bool vis[K];

void getprime(int n){
    for(int i=2;i<=n;++i){
        if(!vis[i])prime[++cnt]=i;
        for(int j=1;j<=cnt&&i*prime[j]<=n;++j){
            vis[i*prime[j]]=true;
            if(i%prime[j]==0)break;
        }
    }
}
int ask(int l,int r,int k){
    int t=Log[r-l+1];
    return max(f[k][l][t],f[k][r-(1<<t)+1][t]);
}
void pushup(int rt){t[rt]=t[ls]*t[rs]%mod;}
void build(int l,int r,int &rt){
    t[rt=++siz]=1;
    if(l==r)return t[rt]=1,void();
    build(l,mid,ls),build(mid+1,r,rs);
}
void alter(int l,int r,int rt,int id,int v){
    if(l==r)return t[rt]=v,void();
    id<=mid?alter(l,mid,ls,id,v):alter(mid+1,r,rs,id,v);
    pushup(rt);
}
void insert(int l,int r,int rt,int& nw,int id,int v){
    nw=++siz,lc[nw]=ls,rc[nw]=rs;
    if(l==r)return t[nw]=v,void();
    id<=mid?insert(l,mid,ls,lc[nw],id,v):insert(mid+1,r,rs,rc[nw],id,v);
    pushup(nw);
}
ll query(int l,int r,int rt,int L,int R){
    if(L<=l&&r<=R)return t[rt]; ll res=1;
    if(L<=mid)res=query(l,mid,ls,L,R);
    if(mid<R)res=res*query(mid+1,r,rs,L,R)%mod;
    return res;
}

signed main(){
    n=read(),getprime(K-1);
    for(int i=1;i<=cnt;++i)
        for(int j=pw[i][0]=1;j<=20;++j)
            pw[i][j]=pw[i][j-1]*prime[i]%mod;
    for(int i=1;i<=n;++i){
        a[i]=read();
        for(int j=1;j<=cnt;++j)
            while(a[i]%prime[j]==0)
                f[j][i][0]++,a[i]/=prime[j];
    }
    q=read();
    for(int i=2;i<=n;++i)Log[i]=Log[i>>1]+1;
    for(int k=1;k<=cnt;++k)for(int i=1;(1<<i)<=n;++i)
        for(int j=1;j+(1<<i)-1<=n;++j)
            f[k][j][i]=max(f[k][j][i-1],f[k][j+(1<<(i-1))][i-1]);

    build(1,n,rot[0]);
    for(int i=1;i<=n;++i){
        if(!pos[a[i]])alter(1,n,rot[0],i,a[i]);
        else b[pos[a[i]]]=i;
        pos[a[i]]=i;
    }
    for(int i=1;i<=n;++i)
        if(b[i])insert(1,n,rot[i-1],rot[i],b[i],a[b[i]]);
        else rot[i]=rot[i-1];
    ll lastans=0,l,r;
    while(q--){
        l=(read()+lastans)%n+1,r=(read()+lastans)%n+1,lastans=1;
        if(l>r)swap(l,r);
        for(int i=1;i<=cnt;++i)
            (lastans*=pw[i][ask(l,r,i)])%=mod;
        lastans=(lastans*query(1,n,rot[l-1],l,r))%mod;
        printf("%lld\n",lastans);
    }
    return 0;
}
/*
3
2 3 5
4
1 3
3 3
2 3
2 3
*/
posted @   _Famiglistimo  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2021-02-11 「JSOI2007」文本生成器 题解
2021-02-11 [POI2006]OKR-Periods of Words 题解
点击右上角即可分享
微信分享提示