CF1493D GCD of an Array(STL+维护质因数)

CF1493D GCD of an Array(STL+维护质因数)

You are given an array a of length n. You are asked to process q queries of the following format: given integers i and x, multiply ai by x.
After processing each query you need to output the greatest common
divisor (GCD) of all elements of the array a.

Since the answer can be too large, you are asked to output it modulo
109+7.

Input The first line contains two integers — n and q (1≤n,q≤2⋅105).

The second line contains n integers a1,a2,…,an (1≤ai≤2⋅105) — the
elements of the array a before the changes.

The next q lines contain queries in the following format: each line
contains two integers i and x (1≤i≤n, 1≤x≤2⋅105).

Output Print q lines: after processing each query output the GCD of
all elements modulo 109+7 on a separate line.
输入样例
4 3
1 6 8 12
1 12
2 3
3 3
输出样例
2
2
6

乍一看题目,觉得这是一道普通的gcd的题目,于是我就把gcd函数先编好了,但是等真正上手的时候会发现gcd可能用不上。最普通也是最容易的想法:暴力。 但是仔细看题意,数组长度为2e5,暴力一遍(即双重for循环去记录任意两个数的gcd)。此时时间复杂度是O(n^2),肯定是过不了的,况且此时所需的值(不一定是最小值)还需要筛选。这时,就需要记住唯一分解定理:任意一个正整数都可以分解成若干个素数的乘积。数学符号表示就是: a=(p1 ^k1) x(p2 ^k2)…x(pn ^kn).(a为任意正整数,p1-pn为素数,k1-kn为指数)。
有了这个定理,是不是就容易多了。现在我们只需要把数组中的每个数分解成若干个素数,用map来维护它的每一个质因数的指数,再用multiset来为维护整个数组的质因数指数(为什么要用multiset呢?当然是因为它可以自动升排序啊,而且还可以维护有重集),这样当对于一个素数x来说,若multiset中记录这个素数的容器大小等于n(即数组中每个数都有该质因数)则只需要将答案ans乘于该容器的头部迭代器对应的值(因为头记录的是最小的指数,意味着数组中每个数至少都可以分解成该质因数的这个指数)。
竟然已经清楚了的话,那就上代码。

#include<bits/stdc++.h>     //维护质因数 
using namespace std;
const int maxn=2e5+10;
const int mod=1e9+7;
int prime[maxn],a[maxn],ans=1,n,q;
multiset<int>s[maxn];        //维护每个质因数的个数 
map<int,int>mp[maxn];              //维护每个数的质因数 
typedef long long ll;
inline int read(){
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*f;
}
inline ll qpow(int base,int power){     //快速幂 
	ll result=1;
	while(power){
		if(power&1) result=(result*base)%mod;
		power>>=1;
		base=(base*base)%mod; 
	}
	return  result%mod;
}
void pre(){
	memset(prime,0,sizeof(prime));
    for(int i=2;i<=maxn-10;i++)
    {
        if(!prime[i]){
        	prime[i]=i;
        	for(int j=2*i;j<=maxn-10;j+=i)  if(!prime[j]) prime[j]=i;
		}
    }
}
void add(int k,int x){
	while(x!=1){
		int div=prime[x],add=0;
		while(x%div==0) add++,x/=div;
		int temp=0,cnt=0;
		temp=mp[k][div];
		mp[k][div]+=add;
		if(s[div].size()==n) cnt=*s[div].begin();
		if(temp!=0) s[div].erase(s[div].find(temp));
		s[div].insert(mp[k][div]);
		if(s[div].size()==n) ans=(ans*qpow(div,*s[div].begin()-cnt))%mod;
	}
}
int main(){
	n=read();q=read();
	pre();
	for(int i=1;i<=n;i++){
		a[i]=read();
		add(i,a[i]);
	} 
	int pos,x;
	for(int i=1;i<=q;i++){
		pos=read();x=read();
		add(pos,x);
		cout<<ans<<endl;
	}
	return 0;
}

函数pre()是一个埃及筛,时间复杂度在O(nlogn),用于筛选每一个正整数的最小质因数。
函数qpow()是一个快速幂函数,用来快速求出指数级的数,时间复杂度在O(log2n),以2为底,这是一个很理想的求指数级的函数,他可以在一秒中就算出一个数的几十亿次方(当然前提是数据范围内)。
关键在于add()函数,他把数组中每一个数都分解成质因数并且记录了指数,同时在最后面还很方便的记录了答案。(请耐心品味)
整体复杂度在O(nlogn),是完全够用的

  • 15
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

&が&

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

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

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

打赏作者

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

抵扣说明:

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

余额充值