Function--线段树维护区间最小值+思维

题目链接:https://cn.vjudge.net/problem/HDU-5875

题目大意

给定一个长度为n的序列,根据上面的公式,求出F(l, r)。

分析

仔细观察后发现,当l==r时,答案是a[l],当l < r时,答案是a[l] % a[l + 1] %...%a[r]。假设一个数x模一个比它大的数,相当于没有模,只有比x小的数才起到了取模的作用。那么我们用线段树维护区间最小值,每次查找(l + 1, r)范围内第一个比x小的数的位置(x初始为a[l]),假设记为pos,如果pos != -1,那么l = pos,x %= a[pos],如果pos == -1,说明后面就没有比x小的数了,就不用了查找了。

代码

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int n, m, a[N];
struct node{
	int l, r;
	int val;
}tr[N<<2];
void pushup(int m)
{
	tr[m].val = min(tr[m<<1].val, tr[m<<1|1].val);
}
void build(int m, int l, int r)
{
	tr[m].l = l;
	tr[m].r = r;
	if(l == r)
	{
		tr[m].val = a[l];
		return ;
	}
	int mid = (l + r) >> 1;
	build(m<<1, l, mid);
	build(m<<1|1, mid + 1, r);
	pushup(m);
}
int ask(int m, int l, int r, int w)
{
	if(tr[m].l == tr[m].r)
	{
		if(tr[m].val <= w)
			return tr[m].l;
		return -1;
	}
	int mid = (tr[m].l + tr[m].r) >> 1;
	if(l <= mid && tr[m<<1].val <= w)
	{
		int tmp = ask(m<<1, l, r, w);
		if(tmp != -1) return tmp;
	}
	if(r > mid && tr[m<<1|1].val <= w)
		return ask(m<<1|1, l, r, w);
	return -1;
}
int main()
{
	int t;
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d", &n);
		for(int i = 1; i <= n; i++)
			scanf("%d", &a[i]);
		build(1, 1, n);
		scanf("%d", &m);
		while(m--)
		{
			int l, r;
			scanf("%d %d", &l, &r);
			int ans = a[l];
			while(l < r)
			{
				int pos = ask(1, l + 1, r, a[l]);
				if(pos == -1) break;
				l = pos;
				ans %= a[pos];
			}
			printf("%d\n", ans);
		}
	}
	return 0;
}

 

你可能感兴趣的:(线段树,思维)