Codeforces Round #705 (Div. 2) D. GCD of an Array(质因数分解 + multiset)

传送门


题目大意

给出 n ( 1 ≤ n ≤ 2 e 5 ) n(1 \leq n \leq 2e5) n(1n2e5)个数的序列 a [   ] a[~] a[ ] m m m次更新,每次更新可以将 a [ i ] a[i] a[i]乘上 x ( 1 ≤ x ≤ 2 e 5 ) x(1 \leq x \leq 2e5) x(1x2e5),输出每次更新后这 n n n个数的 g c d gcd gcd

解题思路

这题咋一看不太难但是细思也不简单。 g c d gcd gcd在质因数分解下的意义为所有的 n n n个数公共质因子的最小幂次相乘,可以预处理最小质因子常数时间复杂度分解质因数。

因为每个数都小于 2 e 5 2e5 2e5,所以仔细思考最坏平均的给每个数的质因数都拉满,最终每个数的质因数种类也不会超过十种,因此内存是够的,既然内存够我们就使用 m u l t i s e t multiset multiset存下每种质因子对应 n n n个数中的个数,仅当 m u l t i s e t multiset multiset的容量为 n n n时才会更新答案;使用 u n o r d e r e d _ m a p unordered\_map unordered_map存下每个数每种因子的对应数量,然后就差不多是模拟题了。


//
// Created by Happig on 2021/3/9.
//
#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

ll ans;
int n, num[maxn];  //存每种质因子的数量
multiset<int> ms[maxn];   //存每种质因子在n个数中的所有出现的数目
int prime[maxn / 3], minp[maxn], cnt;
unordered_map<int, int> vis[maxn];
map<int, int> mp;

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

void euler() {
    cnt = 0;
    for (int i = 1; i < maxn; i++) minp[i] = i;
    for (int i = 2; i < maxn; i++) {
        if (minp[i] == i) prime[++cnt] = i;
        for (int j = 1; j <= cnt && i * prime[j] < maxn; j++) {
            minp[i * prime[j]] = prime[j];
            if (i % prime[j] == 0) break;
        }
    }
}

ll qkp(ll x, ll q, ll p) {
    ll res = 1;
    while (q) {
        if (q & 1) res = res * x % p;
        x = x * x % p;
        q >>= 1;
    }
    return res;
}

void divide(int idx, int x) {
    mp.clear();
    while (x > 1) {
        mp[minp[x]]++;
        x /= minp[x];
    }
    for (auto i:mp) {
        vis[idx][i.first] = i.second, num[i.first]++;
        ms[i.first].insert(i.second);
    }
}

void cal(int idx, int x) {
    mp.clear();
    while (x > 1) {
        mp[minp[x]]++;
        x /= minp[x];
    }
    for (auto i:mp) {
        if (vis[idx][i.first]) {
            int pre = *ms[i.first].begin();
            auto pos = ms[i.first].lower_bound(vis[idx][i.first]);
            ms[i.first].erase(pos);
            vis[idx][i.first] += i.second;
            ms[i.first].insert(vis[idx][i.first]);
            int now = *ms[i.first].begin();
            if (num[i.first] == n) ans = ans * qkp(i.first, now - pre, Mod) % Mod;
        } else {
            vis[idx][i.first] = i.second;
            num[i.first]++;
            if (num[i.first] == n) {
                ms[i.first].insert(i.second);
                int now = *ms[i.first].begin();
                ans = ans * qkp(i.first, now, Mod) % Mod;
            } else ms[i.first].insert(i.second);
        }
    }
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int m, pos, x;
    cin >> n >> m;
    euler();
    for (int i = 1; i <= n; i++) {
        cin >> x;
        divide(i, x);
        if (i == 1) ans = x;
        else ans = gcd(ans, x);
    }
    while (m--) {
        cin >> pos >> x;
        cal(pos, x);
        cout << ans << ENDL;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值