题目链接 : hdu 2711 Lost Cows
题目大意:有n头牛,编号从1到n,排成n行,现在它们的顺序打乱了,每一头牛站一行,现在只知道在它前面几行中比它编号小的头数,站在第一行的牛前面没有东西,所以输入数据从第二行开始,让你求牛此时的序列;
解析:比如 输入序列为 1 2 1 0 ,从左往右分别表示比第二行编号小的牛有1头,比第三行小的有2头....比最后一行小的有0头,我们可以从后往前看,比最后一行小的牛有0头,说明最后一行牛的标号为1,因为1是最小的,假设我们有5头牛,比最后一头小的为2,则编号为3,由此可见,若比最后一头小的数有m,则最后一头的编号始终为m+1,然后再看倒数第二行,都是一样的道理。
这题用线段数维护区间内数的个数,开始与区间长度相同,后来每次从最后往前更新,对应叶子节点的区间值变为0,可以说是查询区间第k大;
#include<iostream>
#include<cstdio>
#define maxn 11111
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int sum[maxn<<2];
int L[maxn],x[maxn];
int cnt;
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1]; //线段树维护的是区间的和,区间的和代表有几个数,更新查询时可以判断第几大
}
void build(int l,int r,int rt)
{
if(l==r)
{
sum[rt]=(r-l+1); //区间初始化为区间的长度
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt); //向上更新
}
void update(int p,int l,int r,int rt)
{
if(l==r)
{
sum[rt]=0; //每次更新,这个数就被用过了,下次不再用,所以这个叶子节点赋值为一
L[cnt++]=l; //记录当前第p大的数为l
return ;
}
int m=(l+r)>>1;
if(p<=sum[rt<<1]) update(p,lson); //区间记录的是区间内数字的个数,p表示第p大的数,也就是p个数,若p比左儿子区间的个数少,说明这个数在做儿子里
else update(p-sum[rt<<1],rson); //否则要减去左儿子的个数,在右儿子里找p-sum[rt<<1]大的数
pushup(rt); //向上更新
}
int query(int l,int r,int rt)
{
if(l==r) return l; //这次询问的目的在于找到排在第一行的数,因为他前面不存在数,也就没有比它小的的数,所以该叶子节点始终没有被更新,值始终为1,只要找到这个一就行
int m=(l+r)>>1;
int ret=0;
if(sum[rt<<1]==1) ret=query(lson); //左儿子区间等于1,往左儿子里查找
else ret=query(rson); //否则往右儿子里查找
return ret;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
cnt=0;
fill(L,L+maxn,0);
build(1,n,1);
for(int i=0;i<n-1;i++) scanf("%d",&x[i]);
for(int i=n-2;i>=0;i--) update(x[i]+1,1,n,1); //从最后一行的开始更新
printf("%d\n",query(1,n,1)); //询问第一行的标号
for(int i=cnt-1;i>=0;i--) printf("%d\n",L[i]);
}
return 0;
}