线段树经典题

Buy Tickets

 POJ – 2828 

Sample Input

4
0 77
1 51
1 33
2 69
4
0 20523
1 19243
1 3890
0 31492

Sample Output

77 33 69 51
31492 20523 3890 19243

 

题意: 插队问题,先给一个n, 然后又n个人进行排队,  左边的数是这个人排队后的位置, 后面的人可能插队, 比如第一个人是第0个位置,第二个人的排队后位置也是0 那么第二个人就将插在第一个人的前面,  变成 第二个人的位置是0, 第一个人的位置是1,右边的数是这个人的分数.也可以说是标记.  最后依次输出从小到大位置的人的标记

题解: 这是线段树的问题. 首先关键是 逆序插入,  后来的人不会随前面的人而改动自己的位置

要将某个人插在第n个位置上,如果这个位置没有人,就插入节点,反之,向后移动至第一个空位

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
const int MAXN = 200000;

int pos[MAXN + 100];
int val[MAXN + 100];
int ans[MAXN + 100];
struct Node{
	int num;//表示位置空位的数量
	int left;
	int right;
}node[(MAXN << 2) + 1000];

void build(int L, int R, int rt){
	node[rt].left = L;
	node[rt].right = R;
	if (L == R){
		node[rt].num = 1;
		return;
	}
	int mid = (L + R) >> 1;
	build(L, mid, rt << 1);
	build(mid + 1, R, rt << 1 | 1);
	node[rt].num = node[rt << 1].num + node[rt << 1 | 1].num;
}
void update(int n, int val, int rt){//传进来的n表示要插入的位置
	if (node[rt].left == node[rt].right){//到这一步说明此处肯定有位置
		node[rt].num = 0;
		ans[node[rt].left] = val;//记录分数
		return;
	}
	if (node[rt << 1].num >= n) update(n, val, rt << 1);//如果左边的空位数量大于n 就向左走
	else update(n - node[rt << 1].num, val, rt << 1 | 1);//如果小于,向右边走n-node[rt<<1](相当于插入右边空位的第n个位置) 
	node[rt].num = node[rt << 1].num + node[rt << 1 | 1].num;
}
int main(){
	int m;
	while (scanf("%d", &m) != EOF){
		for (int i = 1; i <= m; i++) scanf("%d%d", &pos[i], &val[i]);
		build(1, m, 1);
		for (int i = m; i >= 1; i--){
			update(pos[i] + 1, val[i], 1);//这里的pos可以为0 应与node的下标对应
		}
		for (int i = 1; i <= m; i++){
			if (i != 1) printf(" ");
			printf("%d", ans[i]);
		}
		printf("\n");
	}
	return 0;
}


Queue

 CodeForces – 91B 

Examples

Input

6
10 8 5 3 50 45

Output

2 1 0 -1 0 -1 

Input

7
10 4 6 3 2 8 15

Output

4 2 1 0 -1 -1 -1 

Input

5
10 3 1 10 11

Output

1 0 -1 -1 -1 

题意:  求 第i个位置和最后一个位置j满足a[i] > a[j],  求两个位置中间的数的个数

题解:, 线段树经典题目 ,求逆序对,我们可以设线段树中存放的是左树和右树的最小值,  那么如果当前查询的值小于右树所存的右树的最小值,那么这个数一定在左树中,在update的时候应该将已经访问过得点都设为INF 这样就可以避免查找到 比 i 小的值

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
const int INF = 0x3f3f3f3f;
struct Node{
	int minn;
	int index;
}node[(MAXN << 2) + 100];
int a[MAXN + 100];
int num;
int n;
void pushup(int rt){
	node[rt].minn = min(node[rt << 1].minn, node[rt << 1 | 1].minn);
}
void build(int L, int R, int rt){
	if (L == R){
		node[rt].index = num;
		node[rt].minn = a[num++];
		return;
	}
	int mid = (L + R) >> 1;
	build(L, mid, rt << 1);
	build(mid + 1, R, rt << 1 | 1);
	pushup(rt);
}
int query(int position, int L, int R, int rt){
	if (L == R){
		return node[rt].index;
	}
	int mid = (L + R) >> 1;
	int res = 0;
	if (node[rt << 1 | 1].minn < a[position]) return query(position, mid + 1, R, rt << 1 | 1);
	else return query(position, L, mid, rt << 1); 
}
void update(int position, int L, int R, int rt){
	if (L == R){
		a[position] = INF;
		node[rt].minn = INF;
		return;
	}
	int mid = (L + R) >> 1;
	if (position <= mid) update(position, L, mid, rt << 1);
	else update(position, mid + 1, R, rt << 1 | 1);
	pushup(rt);
}
int main(){
	while (scanf("%d", &n) != EOF){
		num = 1;
		for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
		build(1, n, 1);
		int res;
		for (int i = 1; i <= n; i++){
			if (i != 1) printf(" ");
			int j = query(i, 1, n, 1);
			if (j == 1) j = i;
			printf("%d", j - i - 1);
			update(i, 1, n, 1);
		}
		printf("\n");
	}

	return 0;
}

 

点赞