题目大意:
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:和...相交