题目链接: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 <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
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;
}