Codeforces1493D GCD of an Array

GsjzTle發表於2021-03-07

題目連結

點我跳轉

題目大意

給定一個長度為 \(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;
}