CodeForces - 1422F Boring Queries(线段树+主席树)

题目链接:点击查看

题目大意:给出 n 个数组成的数组 a ,再给出 m 次询问,每次询问需要回答区间 [ l , r ] 中所有元素的最小公倍数,强制在线

题目分析:首先考虑多个数的最小公倍数该如何求,因为每个质因子对于最小公倍数的贡献都是相互独立的,所以对于某个质因子 p 来说,可以求出其在所有数字中出现的次数记为 b[ 1 ] , b[ 2 ] ... b[ n ] ,那么质因子 p 对于最小公倍数的贡献就是 p^{max(b[i])},对于所有质因子的贡献求乘积就是所求的最小公倍数了

因为每个元素只有 2e5 这么大,所以考虑开根号,令阈值等于 500,满足 500 * 500 >= 2e5 ,其意义就是,在某个数字中,出现次数大于等于 2 的质因子的大小,一定不会大于 500,这样一来我们就可以将所有的质因子分两类单独计算贡献了:

  1. 如果某个质因子 p 小于等于 500,按照上文的思路,维护出现次数的区间最大值即可
  2. 如果某个质因子 p 大于 500,其贡献非 0 即 1,又因为多个质数的最小公倍数就是其乘积,计算一下区间内有多少个不同的质数,并维护其乘积即可

对于第一种情况,因为 500 以内的质数只有 95 个,所以可以直接开 95 个线段树去维护每个质因子的贡献,维护一下区间最大值即可,对于第二种情况,统计区间内不同的质因子,不难想到使用主席树维护,套上模板随便改改就好了

这可能不是正解,因为递归式的线段树写 T 了,换成循环式的线段树勉强划过去了

代码:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=1e5+100;

const int mod=1e9+7;

int num[N],a[N],last[N<<1],n,seg_cnt;
/*线段树*/
struct Seg_tree
{
	int mmax[262626];
	int base=131072;
	void build()
	{
		for(int i=0;i<n;i++)
			mmax[base+i]=num[i+1];
		for(int i=base-1;i>=1;i--)
			mmax[i]=max(mmax[i<<1],mmax[i<<1|1]);
	}
	int query(int k,int l,int r)
	{
		l+=base;r+=base;
		l--;r--;
		int ans=0;
		while(l<=r)
		{
			if(l&1)
			{
				ans=max(ans,mmax[l]);
				l++;
			}
			if(~r&1)
			{
				ans=max(ans,mmax[r]);
				r--;
			}
			l>>=1;r>>=1;
		}
		return ans;
	}
}seg[105];
/*线段树*/
/*主席树*/
struct Node
{
	int l,r;
	LL sum;
}tree[N*40];

int cnt,root[N];

void update(int pos,int &k,int l,int r,LL val)
{
	tree[cnt++]=tree[k];
	k=cnt-1;
	tree[k].sum=max(tree[k].sum,1LL)*val%mod;
	if(l==r)
		return;
	int mid=l+r>>1;
	if(pos<=mid)
		update(pos,tree[k].l,l,mid,val);
	else
		update(pos,tree[k].r,mid+1,r,val);
}

LL query(int rt,int l,int r,int L,int R)//[l,r]:目标区间,[L,R]:当前区间 
{
	if(R<l||L>r)
		return 1;
	if(L>=l&&R<=r)
		return max(tree[rt].sum,1LL);
	int mid=L+R>>1;
	return query(tree[rt].l,l,r,L,mid)*query(tree[rt].r,l,r,mid+1,R)%mod;
}

void init()
{
	root[0]=0;
	tree[0].l=tree[0].r=tree[0].sum=0;
	cnt=1;
}
/*主席树*/
/*快速幂+逆元*/
LL q_pow(LL a,LL b)
{
	LL ans=1;
	while(b)
	{
		if(b&1)
			ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}

LL inv(LL num)
{
	return q_pow(num,mod-2);
}
/*快速幂+逆元*/
/*埃氏筛*/
bool vis[N<<1];

void P()
{
	for(int i=2;i<=500;i++)
	{
		if(vis[i])
			continue;
		for(int j=1;j<=n;j++)
		{
			num[j]=1;
			while(a[j]%i==0)
			{
				a[j]/=i;
				num[j]*=i;
			}
		}
		seg_cnt++;
		seg[seg_cnt].build();
		for(int j=i+i;j<N<<1;j+=i)
			vis[j]=true;
	}
}
/*埃氏筛*/
int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//	ios::sync_with_stdio(false);
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",a+i);
	P();
	//到此为止剩下的a[i]都是大于500的素数了
	for(int i=1;i<=n;i++)
	{
		root[i]=root[i-1];
		if(a[i]==1)
			continue;
		if(last[a[i]])
			update(last[a[i]],root[i],1,n,inv(a[i]));
		update(i,root[i],1,n,a[i]);
		last[a[i]]=i;
	}
	int m;
	scanf("%d",&m);
	LL last=0;
	while(m--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		l=(l+last)%n+1;
		r=(r+last)%n+1;
		if(l>r)
			swap(l,r);
		LL ans=1;
		for(int i=1;i<=seg_cnt;i++)
			ans=ans*seg[i].query(1,l,r)%mod;
		ans=ans*query(root[r],l,r,1,n)%mod;
		printf("%lld\n",last=ans);
	}















	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108942279
今日推荐