题目大意:
N头牛编号1-N(N ≤ 8000),本来按照从小到大顺序排列,但是不小心在邻居家的酒吧里喝醉了,回家的时候顺序就乱了,主人FJ更加二,他不会直接统计每头牛的编号,而是统计每头牛前面一共有几头牛编号是比当前这头牛小的,现根据该信息求出醉牛从前到后的编号。
注释代码:
/*
* Problem ID : POJ 2182 Lost Cows
* Author : Lirx.t.Una
* Language : C++
* Run Time : 32 ms
* Run Memory : 365 KB
*/
#include <stdio.h>
//maximum number of cows,奶牛的最大数量,8,000 + 1
#define MAXCOWN 8001
//maximum size of segment tree,树中结点的最大数量
//16384 = 1 << ( 1 + ceil( log2(8000) ) )
//数组第一个不放元素(构造完全二叉树的常用手段)
#define MAXSEGN 16384
typedef struct {
int lft;//left,左子树
int rht;//right,右子树
int len;//结点所表示的线段的长度
} Seg;//Segment,线段树的元素
Seg s[MAXSEGN];//segment tree,线段树
int d[MAXCOWN];//data,用于从左到右存放
//每头奶牛之前有几头编号小于其的奶牛的数量
//经过处理后存放最终答案,即奶牛队列的序号排列
void
build( int r, int lft, int rht ) {//二分构造线段树
//root,表示根结点的数组下标
s[r].lft = lft;
s[r].rht = rht;
s[r].len = rht - lft + 1;
if ( lft == rht )
return ;
int lc;//left child,根的左子树的数组下标
//(典型的完全二叉树的求法)
int mid;//middle,区间的中点
lc = r + r;
mid = ( lft + rht ) >> 1;
build( lc, lft, mid );
build( lc + 1, mid + 1, rht );
}
int
query( int r, int k ) {//查询根r所表示的线段中第k大的数
s[r].len--;//每查询出一个,改线段中必然就少了一个数
//因此需要删除该数
//可通过减少该线段的长度来实现
//在后面的递归总可以自动调整
if ( s[r].lft == s[r].rht )//查找的底部,可直接获得
//该数
return s[r].lft;
int lc;
lc = r + r;
if ( k <= s[lc].len )//前半线段第k大
return query( lc, k );
else//或者是后一线段第(k - 前半线段长)大
return query( lc + 1, k - s[lc].len );
}
int
main() {
int n;//奶牛总数
int i;//计数变量
scanf("%d", &n);
build( 1, 1, n );
d[1] = 0;//第一头奶牛前面无奶牛
for ( i = 2; i <= n; i++ )
scanf("%d", d + i);
for ( i = n; i >= 1; i-- )
d[i] = query( 1, d[i] + 1 );
for ( i = 1; i <= n; i++ )
printf("%d\n", d[i]);
return 0;
}
无注释代码:
#include <stdio.h>
#define MAXCOWN 8001
#define MAXSEGN 16384
typedef struct {
int lft;
int rht;
int len;
} Seg;
Seg s[MAXSEGN];
int d[MAXCOWN];
void
build( int r, int lft, int rht ) {
s[r].lft = lft;
s[r].rht = rht;
s[r].len = rht - lft + 1;
if ( lft == rht )
return ;
int lc;
int mid;
lc = r + r;
mid = ( lft + rht ) >> 1;
build( lc, lft, mid );
build( lc + 1, mid + 1, rht );
}
int
query( int r, int k ) {
s[r].len--;
if ( s[r].lft == s[r].rht )
return s[r].lft;
int lc;
lc = r + r;
if ( k <= s[lc].len )
return query( lc, k );
else
return query( lc + 1, k - s[lc].len );
}
int
main() {
int n;
int i;
scanf("%d", &n);
build( 1, 1, n );
d[1] = 0;
for ( i = 2; i <= n; i++ )
scanf("%d", d + i);
for ( i = n; i >= 1; i-- )
d[i] = query( 1, d[i] + 1 );
for ( i = 1; i <= n; i++ )
printf("%d\n", d[i]);
return 0;
}
虚线段树优化:
代码:
/*
* Problem ID : POJ 2182 Lost Cows
* Author : Lirx.t.Una
* Language : C++
* Run Time : 16 ms
* Run Memory : 208 KB
*/
#include <stdio.h>
#define MAXN 8000
#define LFT(T) ( (T) << 1 )
#define RHT(T) ( LFT(T) | 1 )
short seg[MAXN * 3];
short num[MAXN + 1];
void
build( int tree, int lft, int rht ) {
int mid;
seg[tree] = rht - lft + 1;
if ( lft != rht ) {
mid = ( lft + rht ) >> 1;
build( LFT(tree), lft, mid );
build( RHT(tree), mid + 1, rht );
}
}
int
query( int tree, int k, int lft, int rht ) {
int mid;
seg[tree]--;
if ( lft == rht ) return lft;
mid = ( lft + rht ) >> 1;
if ( k <= seg[ LFT(tree) ] ) return query( LFT(tree), k, lft, mid );
else return query( RHT(tree), k - seg[ LFT(tree) ], mid + 1, rht );
}
int
main() {
int n;
int i;
scanf("%d", &n);
build( 1, 1, n );
for ( i = 2; i <= n; i++ ) scanf("%d", num + i);
for ( i = n; i > 0; i-- ) num[i] = query( 1, num[i] + 1, 1, n );
for ( i = 1; i <= n; i++ ) printf("%d\n", num[i]);
return 0;
}
单词解释:
unique:adj, 独特的,独一无二的
cow:n, 奶牛
brand:n, 商标,牌子,烙印
in the range A to B:在A到B的范围内
spectacular:adj, 壮观的,惊人的
display:vt, 显示,炫耀
judgement:n, 判断力
neighborhood:n, 邻近,街坊
watering hole:n, 贩酒处,酒吧
beer:n, 啤酒
line up:排列,整队
regrettalbe:adj, 令人遗憾的
sort:vt, 排序,分类
furthermore:adv, 此外,而且
observe:vt, 观察
determine:vt, 决定
silly:adj, 傻的
statistic:n, 数值统计; adj, 统计的,统计学的
precede:vt, 领先,优于
slot:n, 位置