Codeforces Round #675 (Div. 2) F.Boring Queries(主席树+lcm)

题目

长度为n(n<=1e5)的数组a[],ai在[1,2e5]之间,

q(q<=1e5)个询问,每次给出[l,r],询问[l,r]区间内的ai的lcm,

本题强制在线

思路来源

ustze、Heltion代码

题解

强制在线,n=1e5,所以用主席树,

主席树直接维护区间乘积,所以要将lcm引起的乘积变小的部分预先除掉,

询问[l,r]的时候,直接回答r为右端点的主席树在[l,r]的区间乘积

 

考虑lcm,对于每个质因子p来说,若区间内有两个含p的数p^{a_1},p^{a_2},则lcm=p^{max(a_{1},a_{2})}

lcm应该是几个有这个质因子的数的出现次数最大值,

类似询问区间mex以及可持久化结构的一些套路,让每个值的出现位置尽可能靠右

此处需要维护质因子使其尽可能靠右,具体来说

此处为主席树root[3]的维护过程,

对于每个p的幂次,都维护一个其出现的最右的位置,

只让蓝色的区域在lcm中有贡献,此为代码1

 

而代码2,则是维护了一个栈,栈中放入(质因子的位置,当前还剩的幂次)

如果当前最右可以与左侧的比它小的幂次抵消,则尽可能抵消,

然后将当前最右的幂次放入栈中

代码1

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,mod=1e9+7;
typedef pair<int,int> P; 
#define fi first
#define se second
int n,q,x,y,l,r,lasans;
int a[N],inv[N];
//空间 log(2e5)质因子个数*log(1e5)一条链*n 18*17*1e5 
int root[N],ls[200*N],rs[200*N],lcm[200*N],mxp[N],rig[N],c;
vector<P>p[N],now[N];
int modpow(int x,int n,int mod){
	int res=1;
	for(;n;n>>=1,x=1ll*x*x%mod){
		if(n&1)res=1ll*res*x%mod;
	}
	return res;
}
void upd(int l,int r,int &cur,int las,int pos,int v){
	cur=++c;
    ls[cur]=ls[las];rs[cur]=rs[las];
    lcm[cur]=1ll*lcm[las]*v%mod;
	if(l==r){
        return;
	}
	int mid=(l+r)/2;
	if(pos<=mid)upd(l,mid,ls[cur],ls[las],pos,v);
	else upd(mid+1,r,rs[cur],rs[las],pos,v);
}
int ask(int cur,int l,int r,int ql,int qr){
    if(!cur){
        return 1;
    }
    if(ql<=l && r<=qr){
    	return lcm[cur];
    }
    int mid=(l+r)/2,res=1;
    if(ql<=mid)res=1ll*res*ask(ls[cur],l,mid,ql,qr)%mod;
    if(qr>mid)res=1ll*res*ask(rs[cur],mid+1,r,ql,qr)%mod;
    return res;
}
int main(){
	inv[1]=1;
	lcm[0]=1;
	for(int i=2;i<N;++i){
		inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
		if(!mxp[i]){
			for(int j=i;j<N;j+=i){
				mxp[j]=i;	
			}
		}
	}
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		root[i]=root[i-1];
		for(int v=a[i];v!=1;){
			int x=v,pc=1;
			while(x%mxp[v]==0){
				x/=mxp[v];
				pc*=mxp[v];
				if(rig[pc]){
					upd(1,n,root[i],root[i],rig[pc],inv[mxp[v]]);
				}
				rig[pc]=i;
			}
			upd(1,n,root[i],root[i],rig[pc],pc);
			v=x;
		}
	}
	scanf("%d",&q);
	while(q--){
		scanf("%d%d",&x,&y);
		l=(lasans+x)%n+1;r=(lasans+y)%n+1;
		if(l>r)swap(l,r);
		printf("%d\n",lasans=ask(root[r],1,n,l,r));
	}
    return 0;
}

代码2

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,mod=1e9+7;
typedef pair<int,int> P; 
#define fi first
#define se second
int n,q,x,y,l,r,lasans;
int a[N],inv[N];
int root[N],ls[200*N],rs[200*N],lcm[200*N],c;
vector<P>p[N],now[N];
int modpow(int x,int n,int mod){
	int res=1;
	for(;n;n>>=1,x=1ll*x*x%mod){
		if(n&1)res=1ll*res*x%mod;
	}
	return res;
}
void upd(int l,int r,int &cur,int las,int pos,int v){
	cur=++c;
    ls[cur]=ls[las];rs[cur]=rs[las];
    lcm[cur]=1ll*lcm[las]*v%mod;
	if(l==r){
        return;
	}
	int mid=(l+r)/2;
	if(pos<=mid)upd(l,mid,ls[cur],ls[las],pos,v);
	else upd(mid+1,r,rs[cur],rs[las],pos,v);
}
int ask(int cur,int l,int r,int ql,int qr){
    if(!cur){
        return 1;
    }
    if(ql<=l && r<=qr){
    	return lcm[cur];
    }
    int mid=(l+r)/2,res=1;
    if(ql<=mid)res=1ll*res*ask(ls[cur],l,mid,ql,qr)%mod;
    if(qr>mid)res=1ll*res*ask(rs[cur],mid+1,r,ql,qr)%mod;
    return res;
}
int main(){
	inv[1]=1;
	lcm[0]=1;
	for(int i=2;i<N;++i){
		inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
		if(p[i].empty()){
			for(int j=i;j<N;j+=i){
				int num=0;
				for(int k=j;k%i==0;k/=i){
					num++;
				}
				p[j].push_back(P(i,num));	
			}
		}
	}
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		root[i]=root[i-1];
		for(auto x:p[a[i]]){
			int v=x.fi,cnt=x.se,lef=cnt;
			while(!now[v].empty() && lef){
				int h=min(now[v].back().se,lef);
				lef-=h;now[v].back().se-=h;
				upd(1,n,root[i],root[i],now[v].back().fi,modpow(inv[v],h,mod));
				if(now[v].back().se==0){
					now[v].pop_back();		
				}
			}
			if(cnt){
				upd(1,n,root[i],root[i],i,modpow(v,cnt,mod));
				now[v].push_back(P(i,cnt));
			}
		} 
	}
	scanf("%d",&q);
	while(q--){
		scanf("%d%d",&x,&y);
		l=(lasans+x)%n+1;r=(lasans+y)%n+1;
		if(l>r)swap(l,r);
		printf("%d\n",lasans=ask(root[r],1,n,l,r));
	}
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值