题意:
一个1到n的排列,每次查询在[l,r]这个区间中,任选两个数的最大公约数的最大值。
题解:
对1到n的每个i,找到i,2×i,3×i……的位置,排序后可以得到一串位置,只要查询的区间包含其中两个,答案就至少是i。也就是查询区间包含任意相邻的两个点形成的区间即可。
求出上述的所有位置区间,以右端点为第一关键字和查询区间一起排序(若右端点相同位置区间应放前面)。然后遍历这个数组,当遇到一个位置区间时,将i插入树状数组,下标为左端点,若是查询区间,则查询树状数组中左端点到右端点(直接查到MAXN也可以,因为在右端点之后的还没有插进去)的最大值,就是答案。
注意答案至少为1,且l=r时输出0.
//Time:1125ms
//Memory:8323KB
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <vector>
#define MAXN 50010
#define MAXM 600010
#define MP(x,y) make_pair((x),(y))
#define FI first
#define SE second
using namespace std;
int tree[MAXN],num[MAXN],to[MAXN],sta[MAXN],stop;
pair<pair<int,int>,int > pa[MAXM];
int ans[MAXN];
void add(int h,int num)
{
h=MAXN-1-h;
while(h<MAXN)
{
tree[h]=max(tree[h],num);
h+=h&(-h);
}
}
int query(int h)
{
int ret=0;
h=MAXN-1-h;
while(h>0)
{
ret=max(ret,tree[h]);
h-=h&(-h);
}
return ret;
}
int main()
{
//freopen("/home/moor/Code/input","r",stdin);
int ncase,n,q,top=0;
scanf("%d",&ncase);
while(ncase--)
{
top=0;
memset(tree,0,sizeof(tree));
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&num[i]),to[num[i]]=i;
for(int i=2;i<=n;++i)
{
stop=0;
for(int j=i;j<=n;j+=i)
sta[stop++]=to[j];
if(stop==1) continue;
sort(sta,sta+stop);
for(int j=0;j<stop-1;++j) pa[top++]=MP(MP(sta[j+1],-i),sta[j]);
}
scanf("%d",&q);
for(int i=1;i<=q;++i)
{
int l,r;
scanf("%d%d",&l,&r);
pa[top++]=MP(MP(r,i),l);
}
sort(pa,pa+top);
for(int i=0;i<top;++i)
if(pa[i].FI.SE<0)
add(pa[i].SE,-pa[i].FI.SE);
else if(pa[i].SE==pa[i].FI.FI)
ans[pa[i].FI.SE]=0;
else ans[pa[i].FI.SE]=max(1,query(pa[i].SE));
for(int i=1;i<=q;++i) printf("%d\n",ans[i]);
}
return 0;
}