Splay树(区间第k小)——POJ 2761 Feed the dogs

对应POJ题目:点击打开链接

Feed the dogs

Time Limit: 6000MS   Memory Limit: 65536K
Total Submissions: 16655   Accepted: 5203

Description

Wind loves pretty dogs very much, and she has n pet dogs. So Jiajia has to feed the dogs every day for Wind. Jiajia loves Wind, but not the dogs, so Jiajia use a special way to feed the dogs. At lunchtime, the dogs will stand on
one line, numbered from 1 to n, the leftmost one is 1, the second one is 2, and so on. In each feeding, Jiajia choose an inteval[i,j], select the k-th pretty dog to feed. Of course Jiajia has his own way of deciding the pretty value of each dog. It should
be noted that Jiajia do not want to feed any position too much, because it may cause some death of dogs. If so, Wind will be angry and the aftereffect will be serious. Hence any feeding inteval will not contain another completely, though the intervals may
intersect with each other.

Your task is to help Jiajia calculate which dog ate the food after each feeding.

Input

The first line contains n and m, indicates the number of dogs and the number of feedings.

The second line contains n integers, describe the pretty value of each dog from left to right. You should notice that the dog with lower pretty value is prettier.

Each of following m lines contain three integer i,j,k, it means that Jiajia feed the k-th pretty dog in this feeding.

You can assume that n<100001 and m<50001.

Output

Output file has m lines. The i-th line should contain the pretty value of the dog who got the food in the i-th feeding.

Sample Input

7 2
1 5 2 6 3 7 4
1 5 3
2 7 1

Sample Output

3
2

题意:

给n个数和m个区间查询(每个查询区间之间可能有交集,但不会覆盖),每个查询输出序列的第k小的数。

思路:

据说可用各种树过~

伸展树,首先保存下所有查询,对查询的左边界按升序排序,然后对排序后的每个查询区间建立一颗独立的树,至于第k小,就可以直接计算出来;在为下一个查询区间建树时,如果跟前一次查询区间没有交集,就把整棵树删掉重新建;如果有交集,就可以把跟前一次查询区间没有交集的结点删掉,有交集的留下,再把当前查询区间没有交集的加上。

#include <cstdio>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <string.h>
#include <cmath>
#include <iostream>
#define MAX(x, y) ((x)>(y)?(x):(y))
const int MAXN = 100100;
using namespace std;
typedef int Type;
Type a[MAXN], b[MAXN>>1];

struct Q
{
	int x, y, z, id;
	Q()
	{
		id = x = y = z = 0;
	}
};

Q q[MAXN>>1];

bool cmp(Q q1, Q q2)
{
	if(q1.x != q2.x) return q1.x < q2.x;
	else return q1.y < q2.y;
}

typedef struct TREE
{
	Type val;
    TREE *fa, *l, *r;
    int sz; //以该结点为根的树的总结点数
}Tree;

Tree *mark[MAXN]; //保存结点位置

struct SplayTree
{
	public:
	SplayTree()
	{
		rt = NULL;
		inf = 1000000000;
	}

	void Push_up(Tree *T)
	{
		T->sz = (T->l ? T->l->sz : 0) + (T->r ? T->r->sz : 0) + 1;
	}

	void NewNode(Tree *pre, Tree *&T, Type v)
	{
		T = (Tree *)malloc(sizeof(Tree));
		T->val = v;
		T->sz = 1;
		T->fa = pre;
		T->l = T->r = NULL;
	}

	void Init()
	{
		NewNode(NULL, rt, -inf);
		NewNode(rt, rt->r, inf);
		rt->sz = 2;
	}

	void R_rotate(Tree *x)
	{
		Tree *y = x->fa;
		Tree *z = y->fa;
		Tree *k = x->r;
		y->l = k;
		x->r = y;
		if(z){
			if(y == z->l) z->l = x;
			else z->r = x;
		}
		if(k) k->fa = y;
		y->fa = x;
		x->fa = z;
		Push_up(y);
	}

	void L_rotate(Tree *x)
	{
		Tree *y = x->fa;
		Tree *z = y->fa;
		Tree *k = x->l;
		y->r = k;
		x->l = y;
		if(z){
			if(y == z->r) z->r = x;
			else z->l = x;
		}
		if(k) k->fa = y;
		y->fa = x;
		x->fa = z;
		Push_up(y);
	}

	//寻找第x个数的结点
	Tree *FindTag(int x)
	{
		if(NULL == rt) return NULL;
		Tree *p;
		p = rt;
		Type sum = (p->l ? p->l->sz : 0) + 1;
		while(sum != x)
		{
			if(sum < x){
				p = p->r;
				x -= sum;
			}
			else p = p->l;
			if(NULL == p) break;
			sum = (p->l ? p->l->sz : 0) + 1;
		}
		return p;
	}

	void Splay(Tree *X, Tree *&T)
	{
		Tree *p, *end;
		end = T->fa;
		while(X->fa != end)
		{
			p = X->fa;
			if(end == p->fa){ //p是根结点
				if(X == p->l) R_rotate(X);
				else L_rotate(X);
				break;
			}
			//p不是根结点
			if(X == p->l){
				if(p == p->fa->l){
					R_rotate(p); //LL
					R_rotate(X); //LL
				}
				else{
					R_rotate(X); //RL
					L_rotate(X);
				}
			}
			else{
				if(p == p->fa->r){ //RR
					L_rotate(p);
					L_rotate(X);
				}
				else{ //LR
					L_rotate(X);
					R_rotate(X);
				}
			}
		}
		T = X;
		Push_up(T);
	}

	void Insert(Type *A, int x, int y)
	{
		int i;
		//考验指针技巧的代码
		Tree **link, *p;
		for(i = x; i <= y; i++){
			link = &rt;
			while(*link)
			{
				p = *link;
				if(A[i] < p->val) link = &p->l;
				else link = &p->r;
			}
			NewNode(p, *link, A[i]);
			mark[i] = *link;
			Splay(*link, rt);
		}
	}

	void Delete(Type *A, int x, int y)
	{
		int i;
		Tree *t;
		for(i = x; i <= y; i++){
			t = mark[i];
			Splay(t, rt);
			t = rt->l;
			while(t->r) t = t->r;
			Splay(t, rt->l);
			t = rt;
			rt = rt->l;
			rt->r = t->r;
			free(t);
			rt->r->fa = rt;
			rt->fa = NULL;
			Push_up(rt); //切记更新sz
		}
	}

	void Query(int id, int x)
	{
		x++; //自增1,因为有个-oo结点
		Tree *p;
		p = rt;
		int sum = (p->l ? p->l->sz : 0) + 1;
		while(sum != x)
		{
			if(sum < x){
				p = p->r;
				x -= sum;
			}
			else p = p->l;
			if(NULL == p) break;
			sum = (p->l ? p->l->sz : 0) + 1;
		}
		b[id] = p->val;
	}

	void Show()
	{
		InOrder(rt);
		printf("\n");
	}

	void InOrder(Tree *T)
	{
		if(NULL == T) return;
		InOrder(T->l);
		printf("%d ", T->val);
		InOrder(T->r);
	}

	void Free()
	{
		FreeTree(rt);
	}

	void FreeTree(Tree *T)
	{
		if(NULL == T) return;
		FreeTree(T->l);
		FreeTree(T->r);
		free(T);
	}

	private:
	Type inf;
	Tree *rt;
};

SplayTree spl;

int main()
{
	//freopen("in.txt","r",stdin);
	int n, m, i;
	while(scanf("%d%d", &n, &m) == 2)
	{
		for(i = 1; i <= n; i++)
			scanf("%d", a + i);
		for(i = 1; i <= m; i++){
			scanf("%d%d%d", &q[i].x, &q[i].y, &q[i].z);
			q[i].id = i; //把位置记下方便输出
		}
		sort(q + 1, q + m + 1, cmp);
		for(i = 1; i <= m; i++){
			if(q[i].x <= q[i-1].y){ //有交集
				spl.Delete(a, q[i-1].x, q[i].x - 1);
				spl.Insert(a, q[i-1].y + 1, q[i].y);
			}
			else{
				spl.Free(); //把整棵树删掉
				spl.Init();
				spl.Insert(a, q[i].x, q[i].y);
			}
			spl.Query(q[i].id, q[i].z);
		}
		spl.Free();
		for(i = 1; i <= m; i++)
			printf("%d\n", b[i]);
	}
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-10 06:51:20

Splay树(区间第k小)——POJ 2761 Feed the dogs的相关文章

POJ 2761 Feed the dogs

静态区间第K大,主席树.... Feed the dogs Time Limit: 6000MS   Memory Limit: 65536K Total Submissions: 15491   Accepted: 4780 Description Wind loves pretty dogs very much, and she has n pet dogs. So Jiajia has to feed the dogs every day for Wind. Jiajia loves Wi

POJ 2761 Feed the dogs(树状数组求区间第K大)

题目链接: 戳我 题目大意:Jiajia要为宠物狗,宠物狗按成一排站好(1 < i <= n),第 i 只狗的喜欢程度是 a[i], 之后他会先喂某个区间内第k个 即 n 个数, m个询问,接着是 n个数 接下来 m 行,每行是 l r k即 l 到 r 这个区间第 k 小的数,每个询问输出一个答案,即 a[i] 求区间第k大有很多算法, 详见此博客 [数据结构练习] 求区间第K大数的几种方法 我用的树状数组解法,来自 树状数组从前往后求和,用来解第k大(或小)的数 poj 2985 The

POJ 2761 Feed the dogs (主席树)(K-th 值)

                                                            Feed the dogs Time Limit: 6000MS   Memory Limit: 65536K Total Submissions: 20634   Accepted: 6494 Description Wind loves pretty dogs very much, and she has n pet dogs. So Jiajia has to feed

POJ 2761 Feed the dogs 基础Treap

同样是插入和寻找第k大,这里因为区间不存在包含的情况,所以可以现将区间排序然后直接搞就行了.如果存在包含的情况那就只能上主席树或者是莫队算法来搞了. #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cstdlib> using namespace std; struct Node { Node *ch[2]; int r

poj 2104主席树求区间第k小

POJ - 2104 题意:求区间第k小 思路:无修改主席树 AC代码: #include "iostream" #include "iomanip" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector" #include "set&

少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小

少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小 有一道题(BZOJ 1901)是这样的:n个数,m个询问,询问有两种:修改某个数/询问区间第k小. 不带修改的区间第k小用主席树很好写,不会的同学可以看一下这个. 加上修改怎么做呢?我们可以用数学老师成天讲的类比思想: 可以发现,不修改的区间k小问题中,每加入一个原序列中的数,对应的主席树在上一个的基础上进行修改,而查询的时候用右端点主席树减去左端点左边的主席树.这样的操作就像是维护前缀和:每次加入一个元素的时候,sum[i] =

【XSY2720】区间第k小 整体二分 可持久化线段树

题目描述 给你你个序列,每次求区间第\(k\)小的数. 本题中,如果一个数在询问区间中出现了超过\(w\)次,那么就把这个数视为\(n\). 强制在线. \(n\leq 100000,a_i<n,w\leq n\) 题解 考虑整体二分. 先看看离线要怎么做. 现在我们要计算每个数对每个区间的贡献. 对于每个询问区间和每种数,让这个区间最右边\(w\)个数对这个询问的贡献为\(1\),第\(w+1\)个数对这个询问的贡献为\(-w\). 这样每个数的贡献就是二维平面上的一个矩形.可以用扫描线+线段

HDU 5919 - Sequence II (2016CCPC长春) 主席树 (区间第K小+区间不同值个数)

HDU 5919 题意: 动态处理一个序列的区间问题,对于一个给定序列,每次输入区间的左端点和右端点,输出这个区间中:每个数字第一次出现的位子留下, 输出这些位子中最中间的那个,就是(len+1)/2那个. 思路: 主席树操作,这里的思路是从n到1开始建树.其他就是主席树查询区间第K小,计算区间不同值个数. #include <algorithm> #include <iterator> #include <iostream> #include <cstring&

主席树|求区间第k小模板

主席树 学了主席树,用来求区间上的第k小 写一下自己整理后的模板 求区间第k小 #include<bits/stdc++.h> using namespace std; //求区间第k小 const int maxn = 500010; struct node{ int v,lc,rc; }T[maxn * 21]; int n,m; int root[maxn]; int e; void insert(int pre,int cur,int pos,int l,int r){ if(l ==