做到现在,发现线段数有一个很关键的问题,建树的数组得开的比题目数据大很多,最好开3倍的题目数据范围。
题意:有n个人买票,除了第一个人,后面的n-1个人都会插队,插队的位置为pos[i],每个人有一个值val[i]。
从1~n按顺序输出排完队后的所有人val值。
1.对于前x个人来说,第x+1个人的位置肯定是固定的,所以从后面往前遍历。
2.结构体中pos记录当前区间剩余空间数。
3.若该节点的左儿子空间不够,则遍历右儿子,且所占空间-左儿子的剩余空间//这个规律是看别人的博客知道的,智商不够,理解不了哇T^T。
4.大牛图解
具体还是看代码吧,详细注释:
#include <stdio.h>
#define MAX 600005 //数组开三倍
struct ac
{
int l,r;
int pos; //记录当前区间的剩余空间
}seat[MAX+5];
int n,pos[MAX+5],val[MAX+5]; //存插入位置和val值
int num[MAX+5]; //存输出的值
void build(int l,int r,int k) //标准建树
{
seat[k].l=l;
seat[k].r=r;
seat[k].pos=r-l+1; //剩余空间初始化
if(l==r)
return ;
int mid=(l+r)/2;
build(l,mid,2*k);
build(mid+1,r,2*k+1);
}
void insert(int pos,int val,int k)
{
seat[k].pos--; //当前位置的剩余空间-1
// printf("k:%d pos:%d\n",k,seat[k].pos);
if(seat[k].l==seat[k].r)
{
num[seat[k].l]=val;
seat[k].pos=0; //叶子存了数,剩余空间就为0了,这里改成seat[k].pos--也是一样的
return ;
}
if(pos<=seat[2*k].pos) //若左节点剩余空间足够容纳pos
insert(pos,val,2*k);
else
insert(pos-seat[2*k].pos,val,2*k+1); //否则pos-左节点剩余空间,遍历右节点
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
build(1,n,1); //标准建树
int i;
for(i=1;i<=n;i++)
{
scanf("%d %d",&pos[i],&val[i]);
pos[i]++; //因为该题是从0~n-1,转化成1~n
}
for(i=n;i>=1;i--) //倒叙插入
insert(pos[i],val[i],1);
for(i=1;i<=n;i++)
{
printf("%d",num[i]);
if(i==n)
printf("\n");
else
printf(" ");
}
}
return 0;
}
一开始知道这个是线段树的题,但看到这个题目一点思路都没有,也不知道线段树存什么。
感觉距离完全掌握线段树还有很长的路要走。