2021 Jiangxi Provincial Collegiate Programming Contest G题Magic Number Group - 莫队求区间质因子出现最多的次数

  • 问题很直观,因为一个数的质因子个数很少,而且数字个数和查询次数都是 1 0 4 10^4 104级别,所以考虑直接莫队暴力
  • 此题是经典莫队,使用两个数组分别记录每个质因子的出现次数和出现次数为 i i i的质因子数量,方便统计答案,每当查询的时候,注意左右端点的移动对这两个数组的影响
  • 实测采用基本的排序方式也就是左端点按块从小到大,如果相同则按右端点从小到大排序即可通过;但是如果使用优化的排序方式即左端点所在块为奇数按右端点从小到大(同为奇数则按照块的顺序从小到大),否则按照右端点从大到小这种排序方式能快 80 m s 80ms 80ms左右,后者更优
  • 如果使用 n n n\sqrt n nn 的埃筛预处理出所有的质因子会 T T T掉,这里选择使用求质因子最简单的办法,就是欧拉函数用的那个,求第 i i i个数字的质因子都是谁,这样就不会 T T T掉,就很玄学
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1e6 + 5;
vector<int> fac[N];// 记录第i个数的质因子
int num[N];// 因子i的出现次数
int cnt[N];// 出现次数为i的因子的个数
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int t;
  cin >> t;
  while(t--){
    int n, q;
    cin >> n >> q;
    vector<int> a(n + 1), belong(n + 1);  
    int block = sqrt(n);
    for(int i=1;i<=n;i++){
      cin >> a[i];
      belong[i] = (i - 1) / block + 1;
      fac[i].clear();
      int x = a[i];
      for(int j=2;1ll*j*j<=1ll*x;j++){
        if(x % j == 0){
          while(x % j == 0){
            x /= j;
          }
          fac[i].push_back(j);
        }
      }
      if(x > 1) fac[i].push_back(x);
    }
    vector<array<int, 3> > ask(q);
    for(int i=0;i<q;i++){
      cin >> ask[i][0] >> ask[i][1];
      ask[i][2] = i;
    }sort(ask.begin(), ask.end(), [&](array<int, 3> x, array<int, 3> y){
      // 常规排序
      // return belong[x[0]] == belong[y[0]] ? x[1] < y[1] : belong[x[0]] < belong[y[0]];
      // 奇偶性排序
      return (belong[x[0]] ^ belong[y[0]]) ? belong[x[0]] < belong[y[0]] : ((belong[x[0]] & 1) ? x[1] < y[1] : x[1] > y[1]);
    });
    vector<int> ans(q);
    int res = 0;
    function<void(int)> ADD = [&](int x){
      for(auto j : fac[x]){
        cnt[num[j]] -= 1;
        num[j] += 1;
        cnt[num[j]] += 1;
        res = max(res, num[j]);
      }
    };
    function<void(int)> SUB = [&](int x){
      for(auto j : fac[x]){
        cnt[num[j]] -= 1;
        if(cnt[num[j]] == 0 && num[j] == res){
          res -= 1;
        }
        num[j] -= 1;
        cnt[num[j]] += 1;
      }
    };
    int l = 1;
    int r = 0;
    for(int i=0;i<q;i++){
      while(l > ask[i][0]) ADD(--l);
      while(r < ask[i][1]) ADD(++r);
      while(l < ask[i][0]) SUB(l++);
      while(r > ask[i][1]) SUB(r--);
      ans[ask[i][2]] = res;
    }
    for(int i=0;i<q;i++){
      cout << ans[i] << '\n';
    }
    while(l <= r){
      SUB(l);
      l += 1;
    }
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值