#线段树,约数#洛谷 3889 [GDOI2014]吃

题目

有一个长度为\(n\)的数列,现在有\(m\)组询问每次给出区间\([l,r]\),查询

\[\max_{i,j=1}^n\{gcd(a_i,a_j)[(i<l或i>r)且l\leq j\leq r]\} \]


分析

首先是离线询问,考虑把\(i\)拆成两部分处理
也就是\(\max\{\max\{ans_{i<l}\},\max\{ans_{i>r}\}\}\)
后面这一坨将数列和询问编号翻转就转换成前面这一坨
那就按照区间的左端点递增排序,考虑小于左端点的位置所做的贡献,
处理每个约数下一次出现的位置,如果这个位置出现在区间内,说明这个约数是可以做出贡献的
那么预处理每个位置的数中每个约数下一次出现的位置,并用线段树维护区间最大值即可


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
#define rr register
using namespace std;
const int N=100011;
struct rec{int l,r,rk;}q[N];
int n,m,a[N],ir[N],w[N<<2],h[N];
int ans[N],A[N][131],B[N][131],C[N];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
bool cmp(rec x,rec y){return x.l<y.l;}
inline signed max(int a,int b){return a>b?a:b;}
inline void update(int k,int l,int r,int x,int y){
	if (l==r) {w[k]=max(w[k],y); return;}
	rr int mid=(l+r)>>1;
	if (x<=mid) update(k<<1,l,mid,x,y);
	    else update(k<<1|1,mid+1,r,x,y);
	w[k]=max(w[k<<1],w[k<<1|1]);
}
inline signed query(int k,int l,int r,int x,int y){
	if (l==x&&r==y) return w[k];
	rr int mid=(l+r)>>1;
	if (y<=mid) return query(k<<1,l,mid,x,y);
	else if (x>mid) return query(k<<1|1,mid+1,r,x,y);
	    else return max(query(k<<1,l,mid,x,mid),query(k<<1|1,mid+1,r,mid+1,y));
}
inline void doit(){
	sort(q+1,q+1+m,cmp);
	memset(h,0,sizeof(h));
	memset(w,0,sizeof(w));
	for (rr int i=n,t=0;i;C[i--]=t,t=0)
	    for (rr int j=1;j<=ir[a[i]];++j)
	        if (a[i]%j==0){
		        if (h[j]) A[i][++t]=j,B[i][t]=h[j];
		        h[j]=i;
		        if (j*j<a[i]){
			        if (h[a[i]/j]) A[i][++t]=a[i]/j,B[i][t]=h[a[i]/j];
			        h[a[i]/j]=i;
		        }
	        }
	for (rr int i=1,j=1;i<=m;++i){
		for (;j<q[i].l;++j)
		    for (rr int o=1;o<=C[j];++o)
		        update(1,1,n,B[j][o],A[j][o]);
		ans[q[i].rk]=max(ans[q[i].rk],query(1,1,n,q[i].l,q[i].r));
	}
}
signed main(){
	for (rr int i=1;i<317;++i) ir[i*i]=1;
	for (rr int i=1;i<N;++i) ir[i]+=ir[i-1];
	n=iut(); for (rr int i=1;i<=n;++i) a[i]=iut();
	m=iut(); for (rr int i=1;i<=m;++i) q[i]=(rec){iut(),iut(),i};
	doit();  for (rr int i=1;i<=m;++i) swap(q[i].l,q[i].r);
	for (rr int i=1;i<=m;++i) q[i].l=n-q[i].l+1,q[i].r=n-q[i].r+1;
	reverse(a+1,a+1+n),doit();
	for (rr int i=1;i<=m;++i) print(ans[i]),putchar(10);
	return 0;
}
posted @ 2020-10-15 11:00  lemondinosaur  阅读(68)  评论(0编辑  收藏  举报