Codeforces D.アレイのGCD(複数のgcdをマップおよび設定および維持)(ラウンド#705 Div.2)

ポータル

質問の意味: n個の数とq個のクエリを与え、各クエリはi、xを与え、xをa_ {i}掛けて、すべての数に現れる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;
}

 

おすすめ

転載: blog.csdn.net/Satur9/article/details/114531052