No Pain No Game
题目给出一个序列,多次询问[l,r]区间内的两数最大公因数。
只询问不修改,线段树的离线操作,每次更新一个值a[i]的时候把它因数分解,若存在某个因数x以前出现过,那么就在pre[x](pre[x]表示x这个因数上一次出现的位置)修改值,维护他的最大值,若修改之后存在一个询问区间的r与目前的i相等,那么就区间查找最大值。
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 100010; struct node{int maxi;}tree[N<<2]; #define lson k<<1,l,mid #define rson k<<1|1,mid+1,r struct problem{ int ID,l,r; bool operator < (const problem &rhs)const{return r<rhs.r;} }question[N]; int T,n,m,pre[N],a[N],res[N]; void update(int k,int l,int r,int ID,int x){ if(l == r){ tree[k].maxi = max(tree[k].maxi,x); return; } int mid = (l+r)>>1; if(ID <= mid)update(lson,ID,x); else update(rson,ID,x); tree[k].maxi = max(tree[k<<1].maxi,tree[k<<1|1].maxi); } int query(int k,int l,int r,int xl,int xr){ if(l == xl && r == xr)return tree[k].maxi; int mid = (l+r)>>1; if(xr <= mid)return query(lson,xl,xr); else if(xl > mid)return query(rson,xl,xr); else return max(query(lson,xl,mid),query(rson,mid+1,xr)); } #define clr(a,b) memset(a,b,sizeof(a)) int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); clr(tree,0),clr(pre,0); for(int i = 1;i <= n;++i)scanf("%d",&a[i]); scanf("%d",&m); for(int i = 1;i <= m;++i){ scanf("%d%d",&question[i].l,&question[i].r); question[i].ID = i; } sort(question+1,question+1+m); int ID = 1; for(int i = 1;i <= n;++i){ for(int k = 1;k*k <= a[i];++k){ if(a[i]%k != 0)continue; if(pre[k])update(1,1,n,pre[k],k); if(pre[a[i]/k] && a[i]/k != k)update(1,1,n,pre[a[i]/k],a[i]/k); pre[k] = i,pre[a[i]/k] = i; } while(ID <= m && question[ID].r == i){ res[question[ID].ID] = query(1,1,n,question[ID].l,question[ID].r); ++ID; } } for(int i = 1;i <= m;++i)printf("%d ",res[i]); } return 0; }