質問の意味: n個の数とq個のクエリを与え、各クエリはi、xを与え、xを掛けて、すべての数に現れるgcdを見つけます。
アイデア:
ほとんどのgcdは素因数分解を必要とすることは誰もが知っていますが、この質問はq回尋ねられます。再循環が毎回変更されると、タイムアウトする必要があります。
この質問は、2021年のNiu Ke Winter Holiday Algorithm Basic Training Camp 4のJ質問の基礎に似ています。また、2次元配列を使用して、各数の各素因数の出現数を維持することも考えました(動的マップを使用)。最小の電力数(動的セットを使用すると、最小の数を取得して操作を追加および削除するのに便利です)。
ただし、時間と空間の複雑さを考慮して、コードは実装されていません。ゲーム終了後、兄貴のブログを参照してコードを実装します。具体的なアイデアとコードコメントは次のとおりです。
* gcdに寄与するのは、複数の数の共通の素因数の最小の累乗です。
※したがって、各数の素因数数と各素因数の累乗数を維持し、マップで維持する必要があります。
* n回出現する場合は、この素因数が現時点でgcdに寄与する一般的な素因数であることを意味します。最小の電力を取り、答えを更新します。
*では、更新のq回を維持する方法は?暴力はタイムアウトする必要があります。
*したがって、最初に元の寄与をクリアできます。つまり、ansの寄与を削除し、元の電力番号をクリアしてから、新しい電力番号を入力してから、回答ansを再度更新できます。
*必要に応じて最小値を取得し、数値をすばやく削除してから挿入します。定数が小さく効率が高いものは多重集合です。
コード:
#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ll long long
//#define int long long
#define pii pair<int, int>
#define lowbit(x) (x &(-x))
#define ls(x) x<<1
#define rs(x) (x<<1+1)
#define me(ar) memset(ar, 0, sizeof ar)
#define mem(ar,num) memset(ar, num, sizeof ar)
#define rp(i, n) for(int i = 0, i < n; i ++)
#define rep(i, a, n) for(int i = a; i <= n; i ++)
#define pre(i, n, a) for(int i = n; i >= a; i --)
#define IOS ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
const int way[4][2] = {
{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
using namespace std;
const int inf = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
const ll mod = 1e9+7;
const int N = 2e5 + 5;
inline void read(ll &x){
char t=getchar();
while(!isdigit(t)) t=getchar();
for(x=t^48,t=getchar();isdigit(t);t=getchar()) x=x*10+(t^48);
}
ll n, q, a[N], ans = 1;
unordered_map<int, int> mp[N];
multiset<int> s[N];
int qmi(int a, int k)
{
int res = 1;
while(k){
if(k & 1) res = (ll)res * a % mod;
k >>= 1;
a = (ll)a * a % mod;
}
return res;
}
void add(int id, int a, int b)
{
if(mp[id].count(a)){ //如果a之前就是ai的质因子
if(s[a].size()==n){
ll tmp = qmi(a, *s[a].begin())%mod; //如果这个数原本就出现了n次,先清除答案中质因子a在ai中的贡献
ans = (ans%mod*qmi(tmp, mod-2)%mod)%mod;
}
s[a].erase(s[a].find(mp[id][a])); //先清除质因子a原先在ai中贡献的次方数
mp[id][a] += b;
s[a].insert(mp[id][a]); //再加入新的次方数
if(s[a].size()==n) ans = (ans%mod*qmi(a, *s[a].begin())%mod)%mod; //更新答案
}
else{
mp[id][a] = b; //如果a之前还不是ai的质因子
s[a].insert(b);
if(s[a].size()==n) ans = (ans%mod*qmi(a, *s[a].begin())%mod)%mod; //如果这个数新出现n次,更新答案
}
}
void divide(int id, int x) //质因数分解
{
for(int i = 2; i <= x/i; i ++){
if(x%i==0){
int res = 0;
while(x%i==0){
x /= i;
res ++ ;
}
add(id, i, res); //更新质因子的贡献
}
}
if(x>1) add(id, x, 1);
}
signed main()
{
// IOS;
read(n); read(q);
for(int i = 1; i <= n; i ++){
cin >> a[i];
divide(i, a[i]); //对每个数进行质因数分解
}
while(q --){
ll id , val;
read(id); read(val);
divide(id, val); //对乘上的数进行质因数分解
cout << ans << endl;
}
return 0;
}