一道线段树求排列顺序的简单题.
题目大意:
给一个数N表示有N头牛,农夫先牵出一头(当然不知道是第几头),接下来N-1行表示他把剩下个数中的第i头牛牵了出来.位置从0开始.
问题是问每头牛在队列中的位置.
例如题目样例中:
1,2,1,0
因为从0开始,为了便于理解,我们全部加1:
2,3,2,1
牛的编号: 1 2 3 4 5
第一次牵出第2头,是编号2
1 3 4 5
第二次牵出剩下的第2头,是编号4
1 3 5
第三次牵出剩下的第3头,是编号5
1 3
第四次牵出剩下的第2头,是编号3
1
第五次.....
解题思想和注意事项:
由上例可以看出,只要对牵出的顺序逆序操作,就可模拟反向的操作过程.对于样例中有五头牛的序列
1,1,1,1,1
从最后一位数据开始,将每次牵出的第i头补回当前序列剩余的第i个位置即可.
逆向序列: 1,2,3,2,?
第一回 i=1: 0,1,1,1,1
第二回 i=2: 0,1,0,1,1
第三回 i=3: 0,1,0,1,0
第四回 i=2: 0,1,0,0,0
第五回 最后一回: 编号为,仅剩的位置
由此可以归纳为解求剩余位置中第N位的问题,要求快速查找和更新,我选择的数据结构为线段树.
建树的函数为标准模板建树,每个叶子初值为1,每个结点统计区间和.
更新函数用来求剩余位置中的第i个位置.具体做法将记录的值和左右子树比较,因为给出的位置一定是合理的可找到的,若大于左子树,则减去左子树值递归到右子树.
最后还需要询问函数,询问最后剩下那个位置编号是什么.
源代码:
#include <myhead.h>
const int N=8010;
#define ROOT 1,n,1
int n;
int data[N],result[N];
int seg[N<<2];
inline void pushUp(int rt)
{
seg[rt]=seg[LL(rt)]+seg[RR(rt)];
}
void build(int l,int r,int rt)
{
if(l==r) {
seg[rt]=1;
return ;
}
int mid=MID(l,r);
build(LSON);
build(RSON);
pushUp(rt);
}
void init()
{
build(ROOT);
memset(result,0,sizeof(result));
for(int i=1;i<n;++i)
scanf("%d",&data[i]);
}
void updata(int p,int x,int l,int r,int rt)
{
if(l==r) {
result[x]=l;
seg[rt]=0;
return ;
}
int mid=MID(l,r);
if(seg[LL(rt)]>=p) updata(p,x,LSON);
else updata(p-seg[LL(rt)],x,RSON);
pushUp(rt);
}
void query(int l,int r,int rt)
{
if(l==r) {
result[0]=l;
return ;
}
int mid=MID(l,r);
if(seg[LL(rt)]) query(LSON);
else query(RSON);
}
void work()
{
for(int i=n-1;i>0;--i)
updata(data[i]+1,i,ROOT);
query(ROOT);
for(int i=0;i<n;++i)
printf("%d\n",result[i]);
}
int main()
{
while(~scanf("%d",&n)) {
init();
work();
}
return 0;
}