HDU 4630 离线处理 + 树状数组

传送门 :HDU 4630

题意

给定一个大小为n的排列和m个查询, 输出每个查询区间内最大gcd
最大gcd :区间内任取两个数使其gcd最大的gcd


题解

最初想的是用一个vector存储查询区间的因子信息, 维护出现>=2的因子, T就暂时没想
对于一个区间[l, r]区间内a[l], a[l + 1]…a[r]都用各自的因子, 对于出现过两次以上的因子便是该区间的最大公约数“候选人”, 再从这些候选人找出最大即是最优解
对于a[i]的一个因子tmp, 上次出现的位置(含有tmp因子的数下标)pre[tmp], 则[pre[tmp], i]区间出现一个候选人tmp,可以用tmp更新[1, pre[tmp]]的树状数组(向下更新, 因为(pre[tmp], i]不一定覆盖tmp因子), 用于查询区间r >= i的查询
树状数组存储的是离线处理后尚未查询的区间(r >= i)产生的“候选人”, 更新[1, r]的候选人 ,边更新边查询,待查询的更新只要考虑l的查询就行了
解法就是用离线处理的方式, 更新a[i]的所有因子上一个位置(在该位置更新树状数组), 向上查询, 因为向下更新、向上查询不会超出覆盖


实现

  1. 预处理存储数据范围(5 * 10000)内所有数的因子(vector)
  2. 对查询做r升序排序的处理(要记录下标)
  3. 对于a[i]的所有因子在上一个位置更新树状数组(向下更新)
  4. 查询r = i的区间, 向上查询
  5. 更新查询下标

AC code:

#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;

const int N = 50010;

vector<int> prime[N];
int pre[N], n, a[N], c[N], ans[N];

struct node{
  int l, r, id;
  //int ans;
  bool operator < (const node& tmp){
    return r < tmp.r;
  }
}f[N]; 

inline int lowbit(int x){ return x & (-x); }

void getPrime(){//存储因子
  for(int i = 1; i <= N; ++i)
    for(int j = i; j <= N; j += i)
      prime[j].push_back(i);
}

void add(int x, int v){
  while(x){
    c[x] = max(c[x], v);//更新候选人
    x -= lowbit(x);
  }
}

int getMax(int x){

  int res = 0;

  while(x <= n){
    res = max(res, c[x]);//筛选候选人
    x += lowbit(x);
  }

  return res;
}

int main(){

  getPrime();

  int t, m;
  cin >> t;

  while(t--){
    memset(c, 0, sizeof(c));
    memset(pre, 0, sizeof(pre));
    cin >> n;
    for(int i = 1; i <= n; ++i) cin >> a[i];
    cin >> m;
    for(int i = 1; i <= m; ++i){
      f[i].id = i;
      cin >> f[i].l >> f[i].r;
    }
    sort(f + 1, f + m + 1);//预处理
    int p = 1;
    for(int i = 1; i <= n; ++i){//离线处理
      int x = a[i];
      for(int j = 0; j < prime[x].size(); ++j){
        int tmp = prime[x][j];
        if(pre[tmp]){
          add(pre[tmp], tmp);//更新上一个位置
        }
        pre[tmp] = i;
      }
      while(f[p].r == i && p <= m){
         ans[f[p].id] = getMax(f[p].l);//查询
          ++p;
        }
    }

    for(int i = 1; i <= m; ++i){
      cout << ans[i] << endl;
    }
  }
  return 0;
}

ps : 计蒜客有道一样的。。。 不知道哪里有问题这个代码A不掉, 第一组就错误, 蛋疼

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值