传送门 :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]的所有因子上一个位置(在该位置更新树状数组), 向上查询, 因为向下更新、向上查询不会超出覆盖
实现
- 预处理存储数据范围(5 * 10000)内所有数的因子(vector)
- 对查询做r升序排序的处理(要记录下标)
- 对于a[i]的所有因子在上一个位置更新树状数组(向下更新)
- 查询r = i的区间, 向上查询
- 更新查询下标
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不掉, 第一组就错误, 蛋疼