HDU 5875 Function 线段树 || (ST表 + 二分)

传送门:HDU5875

题意:给定长度为n的一个序列和m次询问,每次询问给出l,r,求a[l] % a[l + 1] % a[l + 2] ... %a[r]。

思路:我们只单纯考虑有效取模的话,那么一个数x最多取模logx次,那么问题是怎么使得每次取模操作都有效,转化为找到一个区间内第一个比x小的数,线段树可以解决,要注意的是用线段树的话常数大,很容易T,可以加一个小优化:当要寻找的区间包含目前结点对应的区间时,我们可以转化为类似二分查找的方式,避免无用的递归,详见:点击打开链接

寻找一个区间内第一个比x小的数也可以ST表 + 二分,ST表预处理区间最小值,然后二分区间长度。详见:点击打开链接

ST表 + 二分是n(logn)^2的复杂度,但即使这样也比不加优化的线段树快。。可想而知线段树常数有多大。


最后要说的是这题最快的解法,那就是暴力,单调栈对于每一个位置预处理出后面数值比它小的第一个位置,然后跳着取模,只需要600+ms,神他妈的随便构造一组递减的样例就能hack掉这种暴力,然而谁让官方数据没有呢。

代码(没加上面说的优化):

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
using namespace std;
typedef pair<int,int> P;
const int MAXN = 100010;
int num[MAXN << 2], a[MAXN];
void build(int l, int r, int rt)
{
	if(l == r)
	{
		scanf("%d", a + l);
		num[rt] = a[l];
		return ;
	}
	int mid = (l + r) >> 1;
	build(lson);
	build(rson);
	num[rt] = min(num[rt << 1], num[rt << 1 | 1]);
}
int query(int L, int R, int aim, int l, int r, int rt)
{
	if(num[rt] > aim) return r + 1;
	if(l == r)
	return l;
	int mid = (l + r) >> 1, ans = r + 1;
	if(L <= mid) ans = query(L, R, aim, lson);
	if(ans <= r) return ans;
	if(R > mid) ans = min(ans, query(L, R, aim, rson));
	return ans;
}
int main()
{
	int T, n, m, l, r, ans;
	cin >> T;
	while(T--)
	{
		scanf("%d", &n);
		build(1, n, 1);
		scanf("%d", &m);
		while(m--)
		{
			scanf("%d %d", &l, &r);
			ans = a[l++];
			while(l <= r && ans)
			{
				l = query(l, r, ans, 1, n, 1);
				if(l <= r) ans %= a[l];
				l++;
			}
			cout << ans << endl;
		}
	}
 	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值