POJ 3145 线段树

题意:

    一个初始为空的序列,两种操作:

    1)A x:将x加入序列尾

    2)B x :查询序列中的一个数A[i], 使 A[i] mod p最小的情况下, i最大, 输出i


思路:

    用线段树维护数值区间内的最小的数是多少。

    1)对于较小的p可以直接暴力,由于数据较水可过。不过其实可以离线处理询问,对于较小的p每加一个数就更新其答案,p相同的询问就可以一起算了(然而懒得写)。

    2)对于较大的p,在区间[0, p - 1], [p, p * 2 - 1] ......内查询最小值即可。由于本题的特性可在线段树的查询时进行一些优化(见代码)。


调了好久,后来发现划分区间查询时的一个细节写错了。。。。。。后来又发现输出写错了。。。。。。


代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>

#define Set(i,j) memset(i, j, sizeof(i))
#define For(i,j,k) for(int i = j;i <= k;i++)
#define Forr(i,j,k) for(int i = j;i >= k;i--)
#define lc (h << 1)
#define rc (h << 1 | 1)
#define M ((L + R) >> 1)

using namespace std;

const int N = 500020, E = 40010;

int n, cnt, A[E], now[N];

struct Segment_Tree{
	int Min[N<<2];

	void init(){
		Set(Min, 0x3f), Set(now, 0);
	}

	void update(int h, int L, int R, int pos){
		Min[h] = min(Min[h], pos);
		if(L == R) return;
		if(pos <= M) update(lc, L, M, pos);
		else update(rc, M+1, R, pos);
	}

	int query(int h, int L, int R, int l, int r){
		if(Min[h] > 1e8 || (l <= L && r >= R)) return Min[h];
		int ret = 2e9;
		if(l <= M) ret = query(lc, L, M, l, r); if(ret <= N) return ret;
		if(r > M) ret = query(rc, M+1, R, l, r);
		return ret;
	}

}Tree;

int Read(){
	char c = getchar();
	for(;c > '9' || c < '0';c = getchar());
	int x = c - '0';
	for(c = getchar();c >= '0' && c <= '9';c = getchar()) x = x * 10 + c - '0';
	return x;
}

void Solve1(int p){
	int Ans = -1, Min = 2e9;
	Forr(i,cnt,1){
		if(A[i] % p < Min) Min = A[i] % p, Ans = i;
		if(!Min) break;
	}
	printf("%d\n", Ans);
}

void Solve2(int p){
	int Ans = 1e9, pos = 1e9;
	For(i,0,N/p){
		int t = Tree.query(1, 0, N, i * p, min(N, (i + 1) * p - 1));
		if(t <= N && t - i * p < Ans) Ans = t - i * p, pos = now[t];
		else if(t <= N && t - i * p == Ans) pos = max(pos, now[t]);
	}
	printf("%d\n", pos);
}

int main(){
	int CASE = 0;
	while(scanf("%d", &n) && n){
		if(CASE) puts("");
		printf("Case %d:\n", ++CASE);
		Tree.init();
		cnt = 0;
		while(n--){
			char Q[5];
			scanf("%s", Q);
			int x = Read();
			if(Q[0] == 'B'){
				Tree.update(1, 0, N, x);
				A[++cnt] = x;
				now[x] = cnt;
			}
			else{
				if(!cnt){
					puts("-1");
					continue;
				}
				if(x <= 3000) Solve1(x);
				else Solve2(x);
			}
		}
	}
	return 0;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值