思路:查询一段区间内所有数字的两两最大的公约数。如果将区间内所有的数字的约数列举出来,那么其中出现过两次或以上的约数,最大的就是所求结果了。对查询按L从大到小排序,每次逆序遍历一个数字时,求出它的所有约数,用pos[s]记录约数s上一次出现的位置,那么当更新到现在这个位置的时候,如果pos非空,说明这时出现两次,将上一次的pos[s]更新到区间内,保证在L一定的情况下,查询R时,若L,R中有s,那么一定至少出现了两次。(纸上模拟一下就明白了)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 50005
int c[N],n,m,now;
int num[N],pos[N],ans[N];
int fac[N],fcnt[N],cnt;
struct Query
{
int l,r,id;
bool operator < (const Query &tmp) const
{
return l>tmp.l;
}
} q[N];
inline int lowbit(int x)
{
return x&(-x);
}
void update(int i,int val)
{
for(; i<=n; i+=lowbit(i))
c[i]=max(val,c[i]);
}
int getmax(int x)
{
int s=0;
for(; x>0; x-=lowbit(x))
s=max(s,c[x]);
return s;
}
void dfs(int x,int s)
{
if(x==cnt)
{
// printf("%d ",s);
if(pos[s]) update(pos[s],s);
pos[s]=now;
return ;
}
int p=1;
for(int i=0; i<=fcnt[x]; i++)
{
dfs(x+1,s*p);
p*=fac[x];
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&num[i]);
scanf("%d",&m);
for(int i=0; i<m; i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q,q+m);
int index=0;
memset(c,0,sizeof(c));
memset(pos,0,sizeof(pos));
for(now=n; now>=1; now--)
{
cnt=0;
// printf("\n%d\n",num[i]);
for(int j=2; j*j<=num[now]; j++)
{
if(num[now]%j==0)
{
fac[cnt]=j;
fcnt[cnt]=0;
while(num[now]%j==0)
{
num[now]/=j;
fcnt[cnt]++;
}
cnt++;
}
}
if(num[now]>1) fac[cnt]=num[now],fcnt[cnt++]=1;
// for(int j=0;j<cnt;j++)
// printf("%d ",fac[j]);
// puts("");
dfs(0,1);
while(q[index].l==now)
{
ans[q[index].id]=getmax(q[index].r);
++index;
}
}
for(int i=0; i<m; i++)
printf("%d\n",ans[i]);
}
return 0;
}