1183. Brackets sequence
表示首次做完一套比赛的题目,HDU2010省赛集训队选拔赛(校内赛),纪念下。

几道线段树水题……hdu2795 ,3303

苗条 posted @ 2010年3月14日 06:09 in acm with tags hdoj2795 ,3303 , 3869 阅读

 在刷hh神牛的线段树专辑,

弄得头晕脑胀,

2795 线段树更新用的是自底向上更新。

表示前面一直想不通10^9的高怎么处理,后来看到只有20W查询,于是就取两者的最小值,因为每个输入高度可以视为1,你高度再多,一张输入就只能占一个高度,用到的也就只有1了。

 

class node {
public:
	int value, l, r, h;
};

class BillBoard {
public:
	void build(int s, int e, int idx);
	void query(int x, int idx);
	void update(int pos, int x);
	void update(int pos, int ls, int rs);
	void hexie();

	int w, n, m;
private:
	int	res, pos, tot;
	node st[800010];
}test;

void BillBoard::build(int s, int e, int idx) {
	st[idx].l = s;
	st[idx].r = e;
	st[idx].value = 0;
	if ( s == e ) {
		st[idx].h = ++tot;
		return;
	}
	int mid = (s+e)>>1;
	build(s, mid, LEFT(idx));
	build(mid+1, e, RIGHT(idx));
}

void BillBoard::query(int x, int idx) {
	if ( st[idx].value+x > w ) {
		res = -1;
		return;
	}
	if ( st[idx].l == st[idx].r ) {
		res = st[idx].h;
		pos = idx;
		return;
	}
	if ( st[LEFT(idx)].value+x <= w ) query(x, LEFT(idx));
	if ( res == -1 ) query(x, RIGHT(idx));
}

void BillBoard::update(int pos, int x) {
	st[pos].value += x;
	int bro;
	if ( pos&1 ) bro = pos-1;
	else		 bro = pos+1;
	if ( pos == 1 ) return;
	update(pos>>1, pos, bro);
}

void BillBoard::update(int pos, int ls, int rs) {
	st[pos].value = min(st[ls].value, st[rs].value);
	int bro;
	if ( pos&1 ) bro = pos-1;
	else		 bro = pos+1;
	if ( pos == 1 ) return;
	update(pos>>1, pos, bro);
}

void BillBoard::hexie() {
	n = n < m ? n : m;
	build(1, n, 1);
	tot = 0;
	int x;
	while ( m-- ) {
		scanf("%d", &x);
		res = -1;
		query(x, 1);
		printf("%d\n", res);
		if ( res != -1 ) {
			update(pos, x);
		}
	}
}

int main() {
	while ( ~scanf("%d%d%d", &test.n, &test.w, &test.m) ) {
		test.hexie();
	}
	return 0;
}

 

hdoj 3303北大oj上也有这题,转载下线段树思路。

一道在线RMQ的应用,题目大意是:有个正整数集合S,初始为空,接下来有一连串的操作:
B X : 表示把整数X(1 <= X <= 500000)放到集合S中。
A Y : 问现在在集合S中的所有整数,模Y结果最小的整数是哪一个,如果集合S中有多个满足条件的整数,输出最后放到S中的那个,如果没有这样的整数,输出-1。

解法比较不常见,要分清况讨论,当Y比较小的时候,直接算:对每一个S中的元素取模,找最小值。当Y比较大的时候,用线段树:因为集合S中的元素都大于1,小于500000,可以把这些数分成这样的区间:
(1, 2, ..., Y-1) (Y, Y + 1, Y + 2, ..., 2Y - 1) (2Y, 2Y + 1, 2Y + 2, ..., 3Y - 1)......
这样每个区间的最小值就是这个区间中模Y最小的那个数,然后再把这些区间的最小值作为候选结果,用最直接的方法:依次对Y取模,找结果。就行了。

做的时候一般想不到分情况讨论,直接暴力poj超时,hdoj过了(杯具),直接线段树的话也会超时,因为思路是把500000按题目输入的除数来分区间,取每个区间的最小值对比,如此这般详见代码。

 

class SegTree {
public:
	int l, r, value, time;
};

class node{
public:
	void build(int s, int e, int idx);
	void query(int s, int e, int idx, int data);
	void update(int data, int idx, int time);
	void hexie();
private:
	SegTree st[4000005];
	int		res, time;
	int		a[1000500];
}test;

int n;

void node::build(int s, int e, int idx) {
	st[idx].l = s;
	st[idx].r = e;
	st[idx].value = -1;
	if ( s == e ) return;
	int mid = (s+e)>>1;
	build(s, mid, LEFT(idx));
	build(mid+1, e, RIGHT(idx));
}

void node::update(int data, int idx, int time) {
	if ( st[idx].l == st[idx].r ) {
		st[idx].value = data;
		st[idx].time = time;
		return;
	}
	if ( st[idx].value == -1 ) {
		st[idx].value = data;
		st[idx].time = time;
		st[LEFT(idx)].value = st[RIGHT(idx)].value = -1;
	} else if ( data < st[idx].value ) {
		st[idx].value = data;
		st[idx].time = time;
	}
	int mid = (st[idx].l + st[idx].r) >> 1;
	if ( data <= mid ) update(data, LEFT(idx), time);
	else update(data, RIGHT(idx), time);
}

void node::query(int s, int e, int idx, int data) {
	if ( st[idx].value == -1 ) return;
	if ( st[idx].l == s && st[idx].r == e ) {
		int temp = st[idx].value%data;
//		printf("%d:%d\n", data, temp);
		if ( res == -1 || res > temp || ( res == temp && time < st[idx].time ) ) {
			res = temp;
			time = st[idx].time;
		}
		return;
	}
	int mid = (st[idx].l + st[idx].r) >> 1;
	if ( e <= mid ) query(s, e, LEFT(idx), data);
	else if ( s > mid ) query(s, e, RIGHT(idx), data);
	else {
		query(s, mid, LEFT(idx), data);
		query(mid+1, e, RIGHT(idx), data);
	}
}

void node::hexie() {
	char	c[5];
	int		i, j, k, tnum;
	st[1].value = -1;
	tnum = 0;
	FF (i, n) {
		scanf("%s%d", c, &k);
		if ( c[0] == 'B' ) {
			a[tnum++] = k;
			update(k, 1, tnum);
		} else if ( k < 5001 ) { //除数小的时候直接暴力
			res = 1000000;
			time = -1;
			FFD(j, tnum) {
				int temp = a[j] % k;
				if ( temp < res ) {
					res = temp;
					time = j+1;
				} 
				if ( res == 0 ) break;
			}
			printf("%d\n", time);
		} else { //这里开始分区间
			res = -1;
			time = -1;
			int a = k;
			int b = k+k-1;
			//特殊情况没有0,因为线段树构造是从1开始的
			query(1, k-1, 1, k);
			while ( a < 500000 ) {
				if ( b > 500000 ) b = 500000;
				query(a, b, 1, k);
				a += k;
				b += k;
			}
			printf("%d\n", time);
		}
	}
}

int main() {
//	freopen("in.txt","r",stdin);
	int T = 0;
	test.build(1, 500000, 1);
	while ( ~scanf("%d%*c", &n) && n ) {
		if ( T++ ) putchar('\n');
		printf("Case %d:\n", T);
		test.hexie();
	}
	return 0;
}

 

 

 

Avatar_small
CrazyAC 说:
2010年7月09日 21:52

表示线段树鸭梨各种大

Avatar_small
Slender 说:
2010年8月24日 09:38

@CrazyAC: 各种鸭梨,好久没来这里了= =


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter