链接
题意
2016大连站网赛的1008。
给出一个长度为N的序列,和Q组询问,对每组询问[l, r],输出a[l] % a[l+1] % … % a[r],l等于r的时候输出a[l]即可。
题解
这题网上貌似有各种解法花式AC,这里给出两种。
第一种是预处理+暴力,对序列a,对a[i]求出其右侧第一个小于等于a[i]的值的下标,因为大于某个余数的值是没有意义的,在查询的时候可以加速计算,跳过一些不必要的值的取模。
但是这种方法的上界是O(n^2)的,比如一个很长的下降序列就可以轻松T掉这种做法,但是在这题里可以<1s AC,效率还是很高的(其实有点坑,早知道这么能过我们也这么写了)。
第二种做法是RMQ+二分,st表维护序列a的最小值,在处理某个查询的时候二分区间,可以查到区间小于等于某值的第一个值(最左侧)的下标,取模后继续查询直到区间右端点即可。
这种做法的复杂度是n*logn*logn的,每次二分区间寻找下一个小于等于该值的值需要logn,一个数在取模的过程中至少降低一半,这个衰减比logn还快,加上区间不停缩短,所以O(n*logn*logn)绝对是充裕的。
值的一提的是我这么做竟然T了,原因是我二分区间的函数是以递归形式写的,我改成迭代形式以后就过了,可见优化常数的重要性啊。。。
代码
单调栈预处理+暴力:
#include <cstdio>
#include <iostream>
using namespace std;
#define next Next
#define maxn (100010)
int a[maxn], next[maxn];
struct _node
{
int idx, value;
} _stack[maxn];
int main()
{
int T;
cin >> T;
while(T--)
{
int N;
cin >> N;
for(int i = 1, pre = -1; i <= N; i++)
{
scanf("%d", &a[i]);
next[i] = 0;
}
for(int i = 1, top = 0; i <= N; i++)
{
while(top && _stack[top-1].value >= a[i])
{
next[_stack[top-1].idx] = i;
top--;
}
_stack[top].idx = i;
_stack[top++].value = a[i];
}
int M;
cin >> M;
for(int i = 0, l, r; i < M; i++)
{
scanf("%d%d", &l, &r);
if(l == r) printf("%d\n", a[l]);
else
{
int now = l, o = a[l];
while(now < r)
{
now = next[now];
if(!now || now > r) break;
o %= a[now];
if(!o) break;
}
printf("%d\n", o);
}
}
}
return 0;
}
RMQ(st表)+二分:
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
#define log2 Log2
#define maxn (100010)
int log2[maxn], st[maxn][32];
void st_prepare(int n, int *arr)
{
log2[1] = 0;
for(int i = 2; i <= n; i++)
{
log2[i] = log2[i-1];
if(i == (1 << log2[i] + 1)) log2[i]++;
}
for(int i = n-1; i >= 0; i--)
{
st[i][0] = arr[i];
for(int j = 1; i + (1 << j) - 1 < n; j++)
{
st[i][j] = min(st[i][j-1], st[i + (1 << j - 1)][j-1]);
}
}
}
inline int st_query(int l, int r)
{
int k = log2[r - l + 1];
return min(st[l][k], st[r - (1 << k) + 1][k]);
}
int st_find(int l, int r, int a)
{
int m;
while(l <= r)
{
if(l == r) return st[l][0] <= a ? l : 0;
m = (l + r) >> 1;
if(st_query(l, m) <= a) r = m;
else if(st_query(m+1, r) <= a) l = m+1;
else return 0;
}
return 0;
/*
if(l == r) return st[l][0] <= a ? l : 0;
int m = (l + r) >> 1, ret = 0;
if(ret = st_find(l, m, a)) return ret;
return st_find(m + 1, r, a);
*/
}
int a[maxn];
int main()
{
int T;
cin >> T;
while(T--)
{
int N;
cin >> N;
for(int i = 1; i <= N; i++)
scanf("%d", &a[i]);
st_prepare(N + 1, a);
int M, l, r, p, q, m, o;
cin >> M;
while(M--)
{
scanf("%d%d", &l, &r);
if(l == r) { printf("%d\n", a[l]); continue; }
p = l + 1, q = r, o = a[l];
while(p <= q)
{
m = st_find(p, q, o);
if(!m) break;
o %= a[m];
if(!o) break;
p = m + 1;
}
printf("%d\n", o);
}
}
return 0;
}