【题解】CF1422F Boring Queries
0:暴力
我们只需要统计区间中,每个素数在一个数中分解出的数量最大值
然后累乘
如果对于每个素数都用ST表统计,几万个ST表肯定会时空双爆
1:根号分治
这么多的素数我们无法存下,我们能不能缩减一下范围呢?我们偶然发现 \(<=\sqrt{N}\) 的素数个数只有 \(87\) 个
考虑根号分治,每个数对于所有素数 \(x\) 满足 \(x<=\sqrt{N}\),进行素因数分解
对于这 \(87\) 个素数开char类型的ST表(防止爆空间)
其他数分解之后,肯定是 \(1\) 或者是 \(> \sqrt{N}\) 的素数,定义此数组为 \(b\)
我们接下来只需要统计区间中数的乘积(重复的数只统计一次)
2:主席树
别想了,肯定不可以算前缀积
我们继续分析
定义 \(pre_i: [1,i-1]\) 中和它数值相等的下标集合的最大值,如果没有则为 \(0\)
设询问区间为 \([L,R]\), 我们发现一个数会被统计,当且仅当其其下标 \(tp\) 满足 $ L<=tp<=R $ 且 $ pre<L $
我们可以对每一个下标 \(g\) 开一颗线段树,如果叶子节点所在下标 \(i\) 的 \(pre_i<=g\) ,则其值为 \(b_i\),否则为 \(1\)
其他节点为其区间的乘积
3:处理
对于 \(<=\sqrt{N}\) 的每个素数,st表算其在 \([L,R]\) 中的每个数分解出的数量最大值,快速幂统计
然后乘上第 \(l-1\) 个线段树上 \([L,R]\) 的乘积
4:代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,sqrtN=sqrt(2*N)+3,SumP=89,logN=18,mod=1e9+7;
int a[N];
vector<int> P;
bool p[sqrtN];
char st[SumP][logN][N];
int n;
void stinit(int tp){
for(int k=1;k<logN;k++){
for(int i=1;i<=n;i++){
st[tp][k][i]=max(st[tp][k-1][i],st[tp][k-1][i+(1<<k-1)]);
}
}
}
int stask(int tp,int l,int r){
int k=log2(r-l+1);
return max(st[tp][k][l],st[tp][k][r-(1<<k)+1]);
}
int now[N*2];
int nxt[N];
int pre[N];
struct node{
int l,r;
int mul;
int son[2];
#define l(q) tree[q].l
#define r(q) tree[q].r
#define mul(q) tree[q].mul
#define ls(q) tree[q].son[0]
#define rs(q) tree[q].son[1]
}tree[N*35];
int idx=0;
int root[N];
void push_up(int q){
mul(q)=1LL*mul(ls(q))*mul(rs(q))%mod;
}
int build(int l,int r){
int q=++idx;
l(q)=l;r(q)=r;
if(l==r){
if(pre[l]==0) mul(q)=a[l];
else mul(q)=1;
return q;
}
int mid=l+r>>1;
ls(q)=build(l,mid);
rs(q)=build(mid+1,r);
push_up(q);
return q;
}
void update(int las,int &now,int tp){
now=++idx;
int l=l(las),r=r(las);
tree[now]=tree[las];
if(l==r){
mul(now)=a[l];
return ;
}
int mid=l+r>>1;
if(tp<=mid) update(ls(las),ls(now),tp);
else update(rs(las),rs(now),tp);
push_up(now);
}
int query(int q,int L,int R){
int l=l(q),r=r(q);
//cout<<q<<" "<<l<<" "<<r<<"\n";
if(L<=l&&r<=R){
//cout<<l<<" "<<r<<" "<<mul(q)<<"\n";
return mul(q);
}
int ans=1;
int mid=l+r>>1;
if(L<=mid) ans*=query(ls(q),L,R);ans%=mod;
if(mid<R) ans*=query(rs(q),L,R);ans%=mod;
//cout<<l<<" "<<r<<" "<<ans<<"\n";
return ans;
}
int ksm(int a,int b){
int res=1;
while(b){
if(b&1){
res*=a;
res%=mod;
}
a*=a;a%=mod;
b>>=1;
}
return res;
}
void dfs(int q){
int l=l(q),r=r(q);
cout<<l<<" "<<r<<" "<<mul(q)<<"\n";
if(l==r) return ;
dfs(ls(q));dfs(rs(q));
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=2;i<sqrtN;i++){
if(!p[i]) P.push_back(i);
for(int j=0;j<P.size()&&P[j]*i<=sqrtN;j++){
p[P[j]*i]=1;
if(i%P[j]==0) break;
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<P.size();j++){
if(a[i]%P[j]==0){
int res=0;
while(a[i]%P[j]==0){
res++;
a[i]/=P[j];
}
st[j][0][i]=res;
}
}
}
for(int i=0;i<SumP;i++) stinit(i);
//for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<"\n";
for(int i=0;i<2*N;i++) now[i]=n+1;
for(int i=n;i>=1;i--){
nxt[i]=now[a[i]];
now[a[i]]=i;
}
for(int i=0;i<2*N;i++) now[i]=0;
for(int i=1;i<=n;i++){
pre[i]=now[a[i]];
now[a[i]]=i;
//cout<<pre[i]<<" ";
}
root[1]=build(1,n);
//dfs(root[1]);
for(int i=2;i<=n;i++){
if(nxt[i-1]==n+1) root[i]=root[i-1];
else update(root[i-1],root[i],nxt[i-1]);
}
//dfs(root[1]);
int m;cin>>m;
int las=0;
//cout<<2<<" "<<P[2]<<" "<<stask(2,1,3)<<"\n";
while(m--){
int l,r;cin>>l>>r;
l=(l+las)%n+1;r=(r+las)%n+1;
if(l>r) swap(l,r);
//cout<<"-->"<<l<<" "<<r<<"\n";
int ans=1;
//cout<<"------>"<<2<<" "<<P[2]<<" "<<stask(2,1,3)<<"\n";
for(int i=0;i<SumP;i++){
//cout<<i<<" "<<P[i]<<" "<<stask(i,l,r)<<"\n";
ans*=ksm(P[i],stask(i,l,r));
ans%=mod;
}
//cout<<ans<<" ";
ans*=query(root[l],l,r);ans%=mod;
//cout<<"-->"<<ans<<"\n";
cout<<ans<<"\n";
las=ans;
}
return 0;
}
5:历程
5.1:山重水复
想用map搞线段树每个区间的素数以及其出现次数,最后8亿次运算被卡掉
其实可以发现可以用数组代替map维护线段树(像是倒退),然后发现时空双爆
5.2:柳暗花明
静态?我用啥线段树,直接转成 \(st\) 表,仍然时空双爆,不过好了很多
考虑缩小范围,发现根号分治
分解之后的数组全部是单个素数或 \(1\)
要统计其累乘,我们分析其性质,发现可以用主席树维护
AC
6:反思
6.1
可以记住其解法,因为这个问题优美简洁,可能作为其他问题的子问题
6.2
有时候的退步只是暂时的,它是迈向成功的必经之路
所以对于一些退步的做法,我们可以保留
6.3
一些复杂问题可能难点只在关键的突破口,如本题根号分治的发现
6.4
一些看起来简单的问题,可能不一定简单,如本题求lcm,和求区间不重乘积
他们看起来很简单,但是其求解是很复杂的

浙公网安备 33010602011771号