Codeforces Round #705 (Div. 2) D. GCD of an Array

题意

给定一个长度为n的数组a,q个操作,每次使第k个数 * x,求每次操作之后的数组所有元素的最大公约数,ans % (1e9 + 7)。

1 <= n ,q<= 2e5  ,1<= ai <= 2e5, 1 <= x <= 2e5

思路

刚开始以为用线段树维护,但是会爆long long,天真的想每次除去这次的gcd,用个变量来记录每次答案,但是每个数也会爆long long。
用multiset来维护每一位的每个质数的个数来变化(方便找最小值,最小值也就是gcd的该为质数个数),用unordered_map来维护上一次这个位这个质数的次数,每次对于每一个质数来说他的变化是知道的,所以每次的答案也是知道的。
操作 求x的质数个数为 根号x,每个质因子都要查set 一个数最多有10多个质因子,所以可以间接忽略 时间复杂度 n * 根号x 差点超时

代码

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<unordered_set>
#include<unordered_map>

using namespace std;
#define x first
#define y second
#define int long long
typedef pair<int ,int > PII;
typedef long long ll;
const int N = 2e5 + 10,mod = 1e9 + 7;
multiset<int >s[N];
unordered_map<int,int > mp[N];
int qmi(int a,int b){
	int res = 1;
	while(b){
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1; 
	}
	return res;
}
signed main(){
	int n,q;
	scanf("%lld%lld",&n,&q);
	int ans = 1;
	for(int i = 1;i <= n;i++){
		int x;
		scanf("%lld",&x);
		for(int j = 2;j <= x / j;j++){
			if(x % j == 0){
				int cnt = 0;
				while(x % j == 0) cnt++,x /= j;
				s[j].insert(cnt);
				mp[i][j] = cnt;
				if(s[j].size() == n) ans = ans * qmi(j,*s[j].begin()) % mod;
			}
		
		}
		if(x > 1) {
			s[x].insert(1);
			mp[i][x] = 1;
			if(s[x].size() == n) ans = ans * qmi(x,1) % mod;
		}
	}
//	cout <<ans <<endl;

	while(q--){
		int k,x;
		scanf("%lld%lld",&k,&x);
		for(int j = 2;j <= x / j;j++){
			if(x % j == 0){
				int cnt = 0;
				int t = 0;
				if(s[j].size() == n) t = *s[j].begin();
				while(x % j == 0) cnt++,x /= j;
				if(mp[k][j] > 0){
					s[j].erase(s[j].find(mp[k][j]));
				}
				mp[k][j] += cnt;
				s[j].insert(mp[k][j]);
				if(s[j].size() == n) ans = ans * qmi(j,*s[j].begin() - t) % mod;
			}
		}
		if(x > 1){
			int t = 0;
			if(s[x].size() == n) t = *s[x].begin();
			if(mp[k][x] > 0){
//				cout << mp[k][x] << endl;
				s[x].erase(s[x].find(mp[k][x]));
			}
			mp[k][x] += 1;
			s[x].insert(mp[k][x]);
			if(s[x].size() == n) ans = ans * qmi(x,*s[x].begin() - t) % mod;
		}
		printf("%lld\n",ans); 
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值