正题
题目链接:https://www.luogu.com.cn/problem/P3889
题目大意
nnn个数的序列,mmm次询问,每次给出一个区间[l,r][l,r][l,r],求在区间内和区间外各选一个数使得他们的gcdgcdgcd最大
解题思路
首先没有修改且没有要求强制在线,考虑离线做法。
我们可以将问题转换为对于求一个与之相交的区间使得该区间两端点的gcdgcdgcd最大。这样我们可以知道如果对于kkk个点有相同的因子,有k(k−1)2\frac{k(k-1)}{2}2k(k−1)个区间,但是只有相邻的点构成的区间会对答案造成影响,所以总共只有k−1k-1k−1个区间
先考虑如何计算在区间的左边选择时的结果。首先我们可以将区间按照右端点从小到大排序,然后用指针扫描右端点,线段树维护该右端点时的左端点的答案。这样我们对于每个数可以枚举他的因子然后在记录前一个该因子的数的位置即可进行维护
时间复杂度O(nnlogn)O(n\sqrt n\log n)O(nnlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10,L=1e5;
int n,m,a[N],pre[N],ans[N];
int w[N*4],lazy[N*4];
struct node{int l,r,num;
}q[N];
bool cmp(node x,node y)
{return x.r<y.r;}
bool cMp(node x,node y)
{return x.l>y.l;}
void Downdata(int x){w[x<<1]=max(w[x<<1],lazy[x]);lazy[x<<1]=max(lazy[x<<1],lazy[x]);w[x<<1|1]=max(w[x<<1|1],lazy[x]);lazy[x<<1|1]=max(lazy[x<<1|1],lazy[x]);return;
}
void Change(int x,int L,int R,int l,int r,int val){if(L==l&&R==r){w[x]=max(w[x],val);lazy[x]=max(lazy[x],val);return;}Downdata(x);int mid=(L+R)>>1;if(r<=mid)Change(x<<1,L,mid,l,r,val);else if(l>mid)Change(x<<1|1,mid+1,R,l,r,val);else Change(x<<1,L,mid,l,mid,val),Change(x<<1|1,mid+1,R,mid+1,r,val);w[x]=max(w[x<<1],w[x<<1|1]);return;
}
int Ask(int x,int L,int R,int pos){if(L==R)return w[x];Downdata(x);int mid=(L+R)>>1;if(pos<=mid)return Ask(x<<1,L,mid,pos);if(pos>mid)return Ask(x<<1|1,mid+1,R,pos);return 0;
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);scanf("%d",&m);for(int i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r),q[i].num=i;sort(q+1,q+1+m,cmp);int z=1;for(int i=1;i<=n;i++){for(int k=1;k*k<=a[i];k++)if(a[i]%k==0){if(pre[k])Change(1,1,L,pre[k]+1,i,k);pre[k]=i;if(a[i]/k==k)continue;if(pre[a[i]/k])Change(1,1,L,pre[a[i]/k]+1,i,a[i]/k);pre[a[i]/k]=i;}while(z<=m&&q[z].r<=i){ans[q[z].num]=Ask(1,1,L,q[z].l);z++;}}memset(w,0,sizeof(w));memset(lazy,0,sizeof(lazy));memset(pre,0,sizeof(pre));sort(q+1,q+1+m,cMp);z=1;for(int i=n;i>=1;i--){for(int k=1;k*k<=a[i];k++)if(a[i]%k==0){if(pre[k])Change(1,1,L,i,pre[k]-1,k);pre[k]=i;if(a[i]/k==k)continue;if(pre[a[i]/k])Change(1,1,L,i,pre[a[i]/k]-1,a[i]/k);pre[a[i]/k]=i;}while(z<=m&&q[z].l>=i){ans[q[z].num]=max(ans[q[z].num],Ask(1,1,L,q[z].r));z++;}}for(int i=1;i<=m;i++)printf("%d\n",ans[i]);return 0;
}