题意
给定一个长度为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;
}