Codeforces D. GCD of an Array (map&set&维护多个数的gcd) (Round #705 Div.2)

68 篇文章 0 订阅
16 篇文章 0 订阅

传送门

题意:给你n个数和q次询问,每次询问给出i,x,让你在a_{i}上乘以x并求出现在所有数的gcd。

思路:

都知道多数的gcd必定需要质因数分解,但是该题询问q次,如若每次都更改再循环的话必定超时。

这个题和2021牛客寒假算法基础集训营4的J题基础有些相似,也想过利用二维数组维护每个数的每个质因子的出现次数(用动态map)和最小次方数(用动态set方便取最小数和增删操作)。

但是考虑到时间空间的复杂度一直没有实现代码,赛后参考大佬博客才将代码实现,具体思路如下图和代码注释:

  *  对gcd有贡献的就是多个数的公共质因子的最小次方数。

  *  所以要维护一个每个数的质因子质因子出现的次数和其每次出现的次方数,用map维护。

  *  如果出现了n次,说明这个质因子此时是对gcd有贡献的公共质因子,取其最小次方数更新答案ans即可。

  *  那么如何维护其q次的更新呢? 暴力必然超时。

  *  所以我们可以把原来的贡献先清除,也就是除掉ans中的贡献,并清除其原先的次方数,再把新的次方数放进去,此时再重新更新答案ans。

  *  需要时时获得最小值,快速删除一个数,然后插入。常数小且效率高的就是multiset.

 

代码实现:

#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;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值