Codeforces 1493D GCD of an Array stl维护

55 篇文章 0 订阅
16 篇文章 0 订阅

文章目录


题目地址

题意

给出一个序列,每次在一个位置上乘一个数,并询问整个数列的 g c d gcd gcd.

题解

所有数字都在 2 × 1 0 5 2\times10^5 2×105之内,我们先筛素数,把每一个数的质因子的个数存放到 v e c t o r vector vector里.
n n n m a p map map存储每个数的各个素因子的个数有多少个.
如果一个质数作为因子出现在所有数中,我们就把它放到台面上,用一个 m u l t i s e t multiset multiset存储每一个数的这个质因子的个数,取 b e g i n begin begin即为最小值,用快速幂乘起来即可.

#pragma GCC optimize("inline","Ofast","no-stack-protector")
#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const int yuzu=2e5,mod=1e9+7;
typedef int fuko[yuzu|10];
fuko val,id,b,cnt,vis,vid;
using pii=pair<int,int>;
vector<pii> g[yuzu|10];
multiset<int> s[yuzu|10];
map<int,int> a[yuzu|10];
ll kasumi(ll a,ll b) {
  ll zw=1;
  for (;b;b>>=1,a=a*a%mod) if (b&1) zw=zw*a%mod;
  return zw;
}
int main() {
  int n,q,i,j,len=0;
  for (i=2;i<=yuzu;++i) {
    if (!vis[i]) {
      for (j=i;j<=yuzu;j+=i) {
        int tmp=0,tmp2=j;
        vis[j]=1;
        for (;tmp2%i==0;++tmp) tmp2/=i;
        g[j].push_back(pii(i,tmp)); // g[j]中的每一个pair的first是j的这个质因子,second是这个数的质因子i的个数.
      }
    }
  }
  read(n),read(q);
  for (i=1;i<=n;++i) {
    read(b[i]);
    for (auto p:g[b[i]]) {
      ++cnt[p.first];
      a[i][p.first]=p.second;
      if (cnt[p.first]==n) { // 含有这个质因数的数的个数达到n,我们需要把这个质因数搬上台面,给他一个编号.
        ++len;
        for (j=1;j<=n;++j) s[len].insert(a[j][p.first]);
        val[len]=p.first;
        vid[p.first]=len;
      }
    }
  }
  for (;q--;) {
    int pos,x;
    read(pos),read(x);
    for (auto p:g[x]) {
      if (a[pos].find(p.first)==a[pos].end()) { //这个位置没有这个质因子.
        ++cnt[p.first];
        a[pos][p.first]=p.second;
        if (cnt[p.first]==n) {
          ++len;
          for (j=1;j<=n;++j) s[len].insert(a[j][p.first]);
          val[len]=p.first;
          vid[p.first]=len;
        } //和输入的时候一样的操作.
      } else {
        if (vid[p.first]) { // 这个质数是有头有脸在所有数中都出现的质数.
          s[vid[p.first]].erase(s[vid[p.first]].find(a[pos][p.first])); // 删掉.
          a[pos][p.first]+=p.second; // 增加.
          s[vid[p.first]].insert(a[pos][p.first]); //再加回来.
        } else {
          a[pos][p.first]+=p.second;
        }
      }
    } 
    ll zw=1;
    for (i=1;i<=len;++i) {
      zw=zw*kasumi(val[i],*s[i].begin())%mod; // 最终答案为所有质因子个数乘方之和.
    }
    printf("%lld\n",zw);
  }
}

这个稳健的代码TLE了.为什么会出问题呢?
问题就在 n = 1 n=1 n=1的时候,每一次相乘都会使一个质因子被搬上台面,因此 l e n len len的大小为质数的总数,则询问的复杂度达到 O ( n q ) O(nq) O(nq),爆炸.
解决这个问题的方法就是不要每次遍历所有 s e t set set,而是直接在操作中对 a n s ans ans进行维护.
修改得代码

#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const int yuzu=2e5,mod=1e9+7;
typedef int fuko[yuzu|10];
fuko val,id,b,cnt,vis,vid;
using pii=pair<int,int>;
vector<pii> g[yuzu|10];
multiset<int> s[yuzu|10];
map<int,int> a[yuzu|10];
ll kasumi(ll a,ll b=mod-2) {
  ll zw=1;
  for (;b;b>>=1,a=a*a%mod) if (b&1) zw=zw*a%mod;
  return zw;
}
int main() {
  int n,q,i,j,len=0;
  for (i=2;i<=yuzu;++i) {
    if (!vis[i]) {
      for (j=i;j<=yuzu;j+=i) {
        int tmp=0,tmp2=j;
        vis[j]=1;
        for (;tmp2%i==0;++tmp) tmp2/=i;
        g[j].push_back(pii(i,tmp));
      }
    }
  }
  ll zw=1;
  read(n),read(q);
  for (i=1;i<=n;++i) {
    read(b[i]);
    for (auto p:g[b[i]]) {
      ++cnt[p.first];
      a[i][p.first]=p.second;
      if (cnt[p.first]==n) {
        ++len;
        for (j=1;j<=n;++j) s[len].insert(a[j][p.first]);
        val[len]=p.first;
        vid[p.first]=len;
        zw=zw*kasumi(val[len],*s[len].begin())%mod;
      }
    }
  }
  for (;q--;) {
    int pos,x;
    read(pos),read(x);
    for (auto p:g[x]) {
      if (a[pos].find(p.first)==a[pos].end()) {
        ++cnt[p.first];
        a[pos][p.first]=p.second;
        if (cnt[p.first]==n) {
          ++len;
          for (j=1;j<=n;++j) s[len].insert(a[j][p.first]);
          val[len]=p.first;
          vid[p.first]=len;
          zw=zw*kasumi(val[len],*s[len].begin())%mod;
        } 
      } else {
        int nid=vid[p.first];
        if (nid) {
          zw=zw*kasumi(kasumi(p.first,*s[nid].begin()))%mod;
          s[nid].erase(s[nid].find(a[pos][p.first]));
          a[pos][p.first]+=p.second;
          s[nid].insert(a[pos][p.first]);
          zw=zw*kasumi(p.first,*s[nid].begin())%mod;
        } else {
          a[pos][p.first]+=p.second;
        }
      }
    } 
    printf("%lld\n",zw);
  }
}

成功AC.
谢谢大家.

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值