題目連結
題目大意
給定一個長度為 \(N\) 的序列 \(A\)
有 \(Q\) 次操作,每次操作給定兩個數 \(i\) , \(X\),使得 \(A[i] = A[i] \times X\)
問每次操作後整個序列的 \(gcd\) 為多少 (對 \(1e9+7\) 取模)
解題思路
顯然 \(gcd\) 不滿足同餘定理 ( \(gcd(4,6) \% 3\) \(!=\) \(gcd(4\%3,6)\%3\) )
而 \(A[i]\) 和 \(X\) 最大值都不超過 \(2e5\) , 所以可考慮質因子分解
首先要知道對於一個數它的質因子個數是 \(log\) 級別的
有個貪心的證明方法
要讓一個數的質因子最多,那這個數的質因子就應該儘可能小
那麼就讓他的質因子為 \(2,3,5,7,11,13,...\)
那麼它就等於 \(2 × 3 × 5 × 7 × 11 × 13 ×...\)
當乘到 \(29\) 時,它已經大於 \(6e9\) 了,所以一個數的質因子個數是 \(log\) 級別的
於是可以用 \(map\) 開個二維動態陣列 \(f[i][j]\),\(f[i][j]\) 表示 \(a[1]\) 的質因子 \(j\) 的冪次
這樣使用的空間最多為 \((N + Q) × log\)
對於一個質數 \(P\) ,它對答案產生貢獻的條件是: $A[1] $ ~ \(A[N]\) 的質因子都包含 \(P\)
也就是 \(P\) 作為質因子一共出現了 \(N\) 次,而它的貢獻顯然是出現過的最小冪次
於是可以對每個質數 \(p\) 開個 \(set\)
當 \(A[i]\) 的質因子包含 \(p\) 時,往 \(set[p]\) 裡插入對應的冪次
而當 \(set[p].size() =n\) 時,\(p\) 就會對答案產生 \(p^{set[p].begin() - pre[p]}\) 貢獻
其中 \(pre[p]\) 表示上一次 \(p\) 對答案產生的貢獻,其初始值為 \(0\)
AC_Code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll pow_mod(ll x,ll n,ll mod)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
int prime[200010] , minprime[200010];
int euler(int n)
{
int c = 0 , i , j;
for(i = 2 ; i <= n ; i ++)
{
if(!minprime[i]) prime[++ c] = i , minprime[i] = i;
for(j = 1 ; j <= c && i * prime[j] <= n ; j ++)
{
minprime[i * prime[j]] = prime[j];
if(i % prime[j] == 0) break ;
}
}
return c;
}
const ll mod = 1e9 + 7;
const int N = 3e5 + 10;
int n , q , I , X , a[N] , pre[N];
map<int , int>f[N];
multiset<int>se[N];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int sum = euler(200000);
ll gcdd = 1;
cin >> n >> q;
for(int i = 1 ; i <= n ; i ++) cin >> a[i];
for(int i = 1 ; i <= n ; i ++)
{
for(int j = 2 ; j * j <= a[i] ; j ++) if(a[i] % j == 0)
{
int c = 0;
while(a[i] % j == 0) a[i] /= j , c ++ ;
f[i][j] = c;
se[j].insert(c);
}
if(a[i] > 1) f[i][a[i]] = 1 , se[a[i]].insert(1);
}
for(int i = 1 ; i <= sum ; i ++)
{
int p = prime[i];
if(se[p].size() == n)
{
auto j = *se[p].begin();
gcdd = gcdd * pow_mod(1LL * p , 1LL * j , mod) % mod;
pre[p] = j;
}
}
while(q --)
{
cin >> I >> X;
for(int j = 1 ; prime[j] * prime[j] <= X && j <= sum ; j ++) if(X % prime[j] == 0)
{
int c = 0 , p = prime[j];
while(X % p == 0) X /= p , c ++ ;
if(f[I].count(p))
{
auto it = se[p].find(f[I][p]);
se[p].erase(it);
}
f[I][p] += c;
se[p].insert(f[I][p]);
if(se[p].size() == n)
{
auto it = *se[p].begin();
gcdd = gcdd * pow_mod(p , it - pre[p] , mod) % mod;
pre[p] = it;
}
}
if(X > 1)
{
if(f[I].count(X))
{
auto it = se[X].find(f[I][X]);
se[X].erase(it);
}
f[I][X] += 1;
se[X].insert(f[I][X]);
if(se[X].size() == n)
{
auto it = *se[X].begin();
gcdd = gcdd * pow_mod(X , it - pre[X] , mod) % mod;
pre[X] = it;
}
}
cout << gcdd << '\n';
}
return 0;
}