hdu4630 No Pain No Game 离线线段树+单点修改

本文介绍了一种求解区间内两数最大公约数(GCD)的高效算法。通过分解因子并利用线段树进行更新查询,实现了离线处理多个询问。适用于竞赛编程中的区间查询问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

求[L, R]上,两个数的gcd的最大值
把[1, R]上每一个数的因子都拆出来
如果某一个因子g上一次出现的位置prev[g] >= L
则因子g可能是最大公约数

我们可以枚举R

这样就是线段树单点更新求区间最大值

关键是更新的位置

如果因子曾出现过,我们就用g去更新线段树在prev[g]上的值

这样要求我们必须离线

#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <queue>
#include <map>
using namespace std;
#define INF 1e9
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define mset(x) memset(x,0,sizeof(x))
typedef __int64 ll;

const int maxn = 50010;
const int maxnode = 50010*4;

int x, x1, x2, val, res;
struct IntervalTree{
	int maxv[maxnode];
	
	void init(){
		mset(maxv);
	}

	void update(int o, int l, int r){
		if(l==r){
			maxv[o] = max(maxv[o], val);
			return;
		}
		int mid = l+(r-l)/2, lc = o*2, rc = o*2+1;
		if(x<=mid) update(lc, l, mid);
		else update(rc, mid+1, r);
		maxv[o] = max( maxv[lc], maxv[rc] );
	}

	void query(int o, int l, int r){
		if(x1<=l && x2>=r){
			res = max(res, maxv[o]);
			return ;
		}
		int mid = l+(r-l)/2, lc = o*2, rc = o*2+1;
		if(x1<=mid)	query(lc, l, mid);
		if(x2>mid) query(rc, mid+1, r);
	}

}tree;

int t, n, m;
int prev[maxn], a[maxn];
vector<int> factor[maxn];

struct operate{
	int l, r, id;
	bool operator< (const operate& a)const {
		return r < a.r;
	}
}op[maxn];
int ans[maxn];

int main(){
//	freopen("a.txt","r",stdin);
//	freopen(".out","w",stdout);
	cin>>t;
	while(t--){
		
		cin>>n;
		rep(i,1,n){
			scanf("%d", &a[i]);
			factor[i].clear();
			for(int k=1; k<sqrt(a[i]+0.5); k++){
				if(a[i]%k==0){
					factor[i].push_back(k);
					if(k*k!=a[i])	factor[i].push_back(a[i]/k);
				}
			}
		}

		cin>>m;
		rep(i,1,m){
			scanf("%d %d", &op[i].l, &op[i].r);
			op[i].id = i;
		}

		sort(op+1, op+m+1);

		tree.init();
		int qcnt=1;
		mset(prev);

		rep(i,1,n){
			for(int j=0; j<factor[i].size(); j++){
				int g = factor[i][j];
				if(prev[g]){
					x = prev[g];
					val = g;
					tree.update(1, 1, n);
				}
				prev[g] = i;
			}
			while(qcnt<=m && op[qcnt].r == i){
				res = 0;
				x1=op[qcnt].l, x2=op[qcnt].r;
				tree.query(1,1,n);
				ans[op[qcnt].id] = res;
				qcnt++;
			}
			if(qcnt>m)	break;
		}

		rep(i,1,m)	printf("%d\n", ans[i]);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值