pku2182: http://poj.org/problem?id=2182
题意:给出n个数(1~n),接下来第2~n行输出n-1个数,第i个数num[i]表示原序列的第i个位置的数ans[i]前面的数中比他小的个数,求原序列。
解法:线段树:先建树,用tot标记该线段中未确定的数的个数,用len表示线段长度,按所给的num从后往前推
code:
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; const int maxn=8010; struct abc { int l,r,len,tot; }v[3*maxn]; int ans[maxn],num[maxn]; void build(int s,int t,int i) //建树 { v[i].l=s; v[i].r=t; v[i].len=t-s+1; v[i].tot=v[i].len; //一开始时所有数都未确定位置,所以tot=len if(s==t) return; int mid=(s+t)/2; build(s,mid,i*2); build(mid+1,t,i*2+1); } int find(int i,int p) { if(v[i].l==v[i].r) { v[i].tot=0; //此时已找到终点,返回该点的数字,因为该数位置此时确定了,所以tot=0 return v[i].l; } int position; if(v[i*2].tot>=p) //表示在左子树 position=find(i*2,p); else //否则在右子树 position=find(i*2+1,p-v[i*2].tot); //减去左子树的tot v[i].tot=v[i*2].tot+v[i*2+1].tot; //从下往上更新 return position; } int main() { int n; while(scanf("%d",&n)!=EOF) { for(int i=2;i<=n;i++) scanf("%d",&num[i]); build(1,n,1); for(int i=n;i>=1;i--) //注意num[1]未赋值,默认为0,求第一个数ans[1]时在其前面的数的个数为0 ans[i]=find(1,num[i]+1); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); } } /*input: 5 1 2 1 0 output: 2 4 5 3 1*/