POJ 2761 Feed the dogs

题目大意:

        Wind爱狗,并且有n条狗(n < 100,001),佳佳不爱狗,但是爱Wind,所以他每天需要替Wind喂狗。当然了,他自有特别的喂狗方式,每到中饭时间够就会按照编号站成一排,从左到有一次为1, 2, 3...n号,每条狗都会被佳佳赋予一个“可爱值”,可爱值互不相同,喂的时候佳佳会先选出一个编号区间[i号-j号],然后在该区间中选择“可爱值”第k大的狗喂食,这样可以避免多次喂同样几条狗导致狗被喂死(这会让Wind大发雷霆,后果很严重)。

        喂食会有m次(m < 50,001),每次都会指定选择区间(i, j)和k(区间内“可爱值”第k大的狗),先求出每次被喂食的狗的“可爱值”,选中的区间可能会相互重叠,只有一个测例。

题目链接

使用规模平衡二叉树SBT(Size Balanced Tree):

注释代码:

/*               
 * Problem ID : POJ 2761 Feed the dogs
 * Author     : Lirx.t.Una               
 * Language   : C++       
 * Run Time   : 2032 ms               
 * Run Memory : 3248 KB               
*/    

#include <stdlib.h>
#include <stdio.h>

//表示树的结点为空
#define	NILL	0

#define	TRUE	1
#define	FALSE	0

//maximum number of dogs
//狗的最大数量
#define	MAXDOGN		100001
//maximum number of feedings
//喂食的最大次数
#define	MAXFEEDN	50001



struct	BFeeding {//Feeding body,
	//关于喂食的结构体

	int		l;//left,喂食的区间左端
	int		r;//right,喂食的区间右端
	int		k;//选择可爱值第k大的狗进行喂食
	int		ord;//ordinal,指明本次是第几次喂食
};

typedef struct BFeeding		Feeding;
typedef	struct BFeeding *	Feed;
typedef	struct BFeeding **	PtFeed;

typedef	int		BOOL;
typedef	int		Tree;//使用数组实现SBT

Feeding		bf[MAXFEEDN];
Feed		f[MAXFEEDN];
int			ans[MAXFEEDN];//存放每次喂食的答案
                  //即每次第k大的狗的可爱值

//以下四项为SBT树中的结点
//其中树结点i中包含了四个域
//key[i]可爱值
//siz[i]即size,即该棵树的大小(总共包含几个结点)
//lft[i]、rht[i],该结点的左右子树
int			key[MAXDOGN] = { 0 };
int			siz[MAXDOGN] = { 0 };
Tree		lft[MAXDOGN] = { NILL };
Tree		rht[MAXDOGN] = { NILL };

int			dog[MAXDOGN];//dog[i]表示第i条狗的可爱值

Tree
RotL(Tree k2) {//AVL左旋

	Tree k1;

	k1		= lft[k2];
	lft[k2]	= rht[k1];
	rht[k1]	= k2;

	siz[k1]	= siz[k2];
	siz[k2] = siz[ lft[k2] ] + siz[ rht[k2] ] + 1;

	return k1;
}

Tree
RotR(Tree k2) {//AVL右旋

	Tree k1;

	k1		= rht[k2];
	rht[k2] = lft[k1];
	lft[k1]	= k2;

	siz[k1]	= siz[k2];
	siz[k2]	= siz[ lft[k2] ] + siz[ rht[k2] ] + 1;

	return k1;
}

Tree
RotLR(Tree t) {//AVL左右双旋

	rht[t] = RotL( rht[t] );

	return RotR(t);
}

Tree
RotRL(Tree t) {//AVL右左双旋

	lft[t] = RotR( lft[t] );

	return RotL(t);
}

Tree
Maintain( Tree tree, BOOL cmp ) {//维护
	//SBT的插入其实都是普通的BST插入,在每次
	//插入后都会使用该函数对SBT树进行维护
	//以调整它的高度,使之平衡

	if ( cmp )//表示插入的可爱值k小于tree结点的可爱值
		//此时该结点必定插入到了tree的左子树,因此需要
		//检查左子树是否过大

		if ( siz[ lft[ lft[tree] ] ] > siz[ rht[tree] ] )
			tree = RotL(tree);//若左子树的左子树规模大于右子树则需左旋
		else
			if ( siz[ rht[ lft[tree] ] ] > siz[ rht[tree] ] )
				tree = RotRL(tree);//若左子树的右子树规模大于右子树则需右左旋
			else
				return tree;//否则就表示满足平衡条件,可以直接退出函数
	else//对于插入到右子树的情形也是相同的
		if ( siz[ rht[ rht[tree] ] ] > siz[ lft[tree] ] )
			tree = RotR(tree);
		else
			if ( siz[ lft[ rht[tree] ] ] > siz[ lft[tree] ] )
				tree = RotLR(tree);
			else
				return tree;

	//即使旋转过也并不代表概述一定就满足平衡条件了,需要继续探测并调整


	//当旋转后左右子仍存在的,则继续向树的最外两侧探测和调整
	if ( lft[tree] )
		lft[tree] = Maintain( lft[tree], TRUE );

	if ( rht[tree] )
		rht[tree] = Maintain( rht[tree], FALSE );

	//此时左右子树已经完美平衡了,但顶点本身可能离平衡只有微小的距离
	//因此此次维护只是做一点点微调,不会耗多少时间的
	tree = Maintain( tree, TRUE );//左微调一次

	return Maintain( tree, FALSE );//最后再右微调一次
}

Tree
Insert( Tree tree, int node, int k ) {

	if ( !tree ) {
	
		key[node] = k;
		siz[node] = 1;

		lft[node] = NILL;
		rht[node] = NILL;

		return node;
	}

	siz[tree]++;

	if ( k < key[tree] )
		lft[tree] = Insert( lft[tree], node, k );
	else
		rht[tree] = Insert( rht[tree], node, k );

	//到这之前都是很平常的BST插入
	return Maintain( tree, k < key[tree] );
}

Tree
Remove( Tree tree, int k ) {

	if ( !tree )
		return NILL;

	siz[tree]--;//由于题目的限制,每次删除的结点
	     //必定都在树中,因此不需要再写Find函数判断
	     //待删的结点是否在树中了,因此可以直接--

	if ( k == key[tree] ) {//若当前结点就是待删结点
	
		if ( !lft[tree] || !rht[tree] )//对于左右子树有空的
			return lft[tree] + rht[tree];//直接返回非空的
		                  //或者两者都是空的,则就直接删除即可

		int node;

		//若左右子树都非空,则可以将左子树中最大的结点
		  //即右下角的结点作为根结点(树顶)代替老的树顶
		  //然后再删除该结点就行了

		//先找到该结点(由于它是左子树中最大的结点,因此可以作为新树顶)
		node = lft[tree];
		while ( rht[node] )
			node = rht[node];

		key[tree] = key[node];//替代
		lft[tree] = Remove( lft[tree], key[node] );//再删除该结点

		return tree;
	}

	if ( k < key[tree] )
		lft[tree] = Remove( lft[tree], k );
	else
		rht[tree] = Remove( rht[tree], k );

	return tree;
}

int
Rank( Tree tree, int k ) {//按照可爱值大小排名

	int		mid;

	mid = siz[ lft[tree] ] + 1;//树顶是第mid大的

	if ( k == mid )
		return key[tree];

	if ( k < mid )
		return Rank( lft[tree], k );
	else
		return Rank( rht[tree], k - mid );
}

int
fcmp(const void *a, const void *b) {

	Feed	fa, fb;

	fa = *(PtFeed)a;
	fb = *(PtFeed)b;

	//排序时按照喂食区间的左端从小到大排
	if ( fa->l - fb->l )
		return fa->l - fb->l;
	else//若左端相等则按照右端从小到大排
		return fa->r - fb->r;
}

int
main() {

	int		n, m;//狗总数和喂食总次数
	int		i, j;//计数变量
	int		l, r;//先区间left - right
	int		pl, pr;//上衣区间previous left - previous right

	int		node;//插入的结点号
	Tree	tree;//SBT树

	scanf("%d%d", &n, &m);
	for ( i = 1; i <= n; i++ )	
		scanf("%d", dog + i);
	for ( i = 1; i <= m; i++ ) {
	
		scanf("%d%d%d", &bf[i].l, &bf[i].r, &bf[i].k);
		bf[i].ord = i;
		f[i]	  = bf + i;//利用指针数组对原数组排序,省时间
	}
	//具体做法是对于每个区间,都构建一个该区间的SBT树
	//然后对该数求rank,即可得到第k大的狗了
	//但是为了避免重复构建相同区间的SBT(或者是重合的)
	//对喂食进行排序(左端从小到大,若相同则右端从小到大)
	  //可以避免对重叠的区间重复构造SBT树
	qsort(f + 1, m, sizeof(Feed), &fcmp);

	//初始化
	tree = NILL;
	node = 0;
	pl	 = 1;
	pr	 = 0;

	for ( i = 1; i <= m; i++ ) {
	
		l = f[i]->l;
		r = f[i]->r;

		//×表示该区间需要删除
		//*表示该区间需要构建
		//空表示该区间什么都不用做
		//不变表示该区间保留,无需删除和构建
		if ( l > pr ) {// pl×××pr  空  l****r
		
			for ( j = pl; j <= pr; j++ )
				tree = Remove( tree, dog[j] );
			for ( j = l; j <= r; j++ )
				tree = Insert( tree, ++node, dog[j] );
		}
		else
			if ( r > pr ) {// pl×××l 不变 pr****r
			
				for ( j = pl; j < l; j++ )
					tree = Remove( tree, dog[j] );
				for ( j = pr + 1; j <= r; j++ )
					tree = Insert( tree, ++node, dog[j] );
			}
			else {// pl×××l 不变 r×××pr
			
				for ( j = pl; j < l; j++ )
					tree = Remove( tree, dog[j] );
				for ( j = pr + 1; j < r; j++ )
					tree = Remove( tree, dog[j] );
			}

		pl = l;
		pr = r;

		ans[ f[i]->ord ] = Rank( tree, f[i]->k );
	}

	for ( i = 1; i <= m; i++ )
		printf("%d\n", ans[i]);

	return 0;
}

无注释代码:

#include <stdlib.h>
#include <stdio.h>

#define	NILL	0
#define	TRUE	1
#define	FALSE	0

#define	MAXDOGN		100001
#define	MAXFEEDN	50001

struct	BFeeding {

	int		l;
	int		r;
	int		k;
	int		ord;
};

typedef struct BFeeding		Feeding;
typedef	struct BFeeding *	Feed;
typedef	struct BFeeding **	PtFeed;

typedef	int		BOOL;
typedef	int		Tree;

Feeding		bf[MAXFEEDN];
Feed		f[MAXFEEDN];
int			ans[MAXFEEDN];

int			key[MAXDOGN] = { 0 };
int			siz[MAXDOGN] = { 0 };
Tree		lft[MAXDOGN] = { NILL };
Tree		rht[MAXDOGN] = { NILL };

int			dog[MAXDOGN];

Tree
RotL(Tree k2) {

	Tree k1;

	k1		= lft[k2];
	lft[k2]	= rht[k1];
	rht[k1]	= k2;

	siz[k1]	= siz[k2];
	siz[k2] = siz[ lft[k2] ] + siz[ rht[k2] ] + 1;

	return k1;
}

Tree
RotR(Tree k2) {

	Tree k1;

	k1		= rht[k2];
	rht[k2] = lft[k1];
	lft[k1]	= k2;

	siz[k1]	= siz[k2];
	siz[k2]	= siz[ lft[k2] ] + siz[ rht[k2] ] + 1;

	return k1;
}

Tree
RotLR(Tree t) {

	rht[t] = RotL( rht[t] );

	return RotR(t);
}

Tree
RotRL(Tree t) {

	lft[t] = RotR( lft[t] );

	return RotL(t);
}

Tree
Maintain( Tree tree, BOOL cmp ) {

	if ( cmp )
		if ( siz[ lft[ lft[tree] ] ] > siz[ rht[tree] ] )
			tree = RotL(tree);
		else
			if ( siz[ rht[ lft[tree] ] ] > siz[ rht[tree] ] )
				tree = RotRL(tree);
			else
				return tree;
	else
		if ( siz[ rht[ rht[tree] ] ] > siz[ lft[tree] ] )
			tree = RotR(tree);
		else
			if ( siz[ lft[ rht[tree] ] ] > siz[ lft[tree] ] )
				tree = RotLR(tree);
			else
				return tree;

	if ( lft[tree] )
		lft[tree] = Maintain( lft[tree], TRUE );

	if ( rht[tree] )
		rht[tree] = Maintain( rht[tree], FALSE );

	tree = Maintain( tree, TRUE );

	return Maintain( tree, FALSE );
}

Tree
Insert( Tree tree, int node, int k ) {

	if ( !tree ) {
	
		key[node] = k;
		siz[node] = 1;

		lft[node] = NILL;
		rht[node] = NILL;

		return node;
	}

	siz[tree]++;

	if ( k < key[tree] )
		lft[tree] = Insert( lft[tree], node, k );
	else
		rht[tree] = Insert( rht[tree], node, k );

	return Maintain( tree, k < key[tree] );
}

BOOL
Find( Tree tree, int k ) {

	if ( !tree )
		return FALSE;

	if ( k == key[tree] )
		return TRUE;

	if ( k < key[tree] )
		return Find( lft[tree], k );
	else
		return Find( rht[tree], k );
}

Tree
Remove( Tree tree, int k ) {

	if ( !tree )
		return NILL;

	siz[tree]--;

	if ( k == key[tree] ) {
	
		if ( !lft[tree] || !rht[tree] )
			return lft[tree] + rht[tree];

		int node;

		node = lft[tree];
		while ( rht[node] )
			node = rht[node];

		key[tree] = key[node];
		lft[tree] = Remove( lft[tree], key[node] );

		return tree;
	}

	if ( k < key[tree] )
		lft[tree] = Remove( lft[tree], k );
	else
		rht[tree] = Remove( rht[tree], k );

	return tree;
}

int
Rank( Tree tree, int k ) {

	int		mid;

	mid = siz[ lft[tree] ] + 1;

	if ( k == mid )
		return key[tree];

	if ( k < mid )
		return Rank( lft[tree], k );
	else
		return Rank( rht[tree], k - mid );
}

int
fcmp(const void *a, const void *b) {

	Feed	fa, fb;

	fa = *(PtFeed)a;
	fb = *(PtFeed)b;

	if ( fa->l - fb->l )
		return fa->l - fb->l;
	else
		return fa->r - fb->r;
}

int
main() {

	int		n, m;
	int		i, j;
	int		l, r;
	int		pl, pr;

	int		node;
	Tree	tree;

	scanf("%d%d", &n, &m);
	for ( i = 1; i <= n; i++ )	
		scanf("%d", dog + i);
	for ( i = 1; i <= m; i++ ) {
	
		scanf("%d%d%d", &bf[i].l, &bf[i].r, &bf[i].k);
		bf[i].ord = i;
		f[i]	  = bf + i;
	}
	qsort(f + 1, m, sizeof(Feed), &fcmp);

	tree = NILL;
	node = 0;
	pl	 = 1;
	pr	 = 0;

	for ( i = 1; i <= m; i++ ) {
	
		l = f[i]->l;
		r = f[i]->r;

		if ( l > pr ) {
		
			for ( j = pl; j <= pr; j++ )
				tree = Remove( tree, dog[j] );
			for ( j = l; j <= r; j++ )
				tree = Insert( tree, ++node, dog[j] );
		}
		else
			if ( r > pr ) {
			
				for ( j = pl; j < l; j++ )
					tree = Remove( tree, dog[j] );
				for ( j = pr + 1; j <= r; j++ )
					tree = Insert( tree, ++node, dog[j] );
			}
			else {
			
				for ( j = pl; j < l; j++ )
					tree = Remove( tree, dog[j] );
				for ( j = pr + 1; j < r; j++ )
					tree = Remove( tree, dog[j] );
			}

		pl = l;
		pr = r;

		ans[ f[i]->ord ] = Rank( tree, f[i]->k );
	}

	for ( i = 1; i <= m; i++ )
		printf("%d\n", ans[i]);

	return 0;
}

单词解释:

pretty:adj, 漂亮的,可爱的

lunchtime:n, 午饭时间

leftmost/rightmost:adj, 最左边的/左右边的

afterefftect:n, 后果

hence:adj, 因此

intersect with:和...相交

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值