题目链接:poj_2104 K-th Number
首先我们要知道Tree中所存储的东西,Tree.sum是它所代区间中有几个数,Tree.l 是它的左区间所在结点的下标,Tree.r 是它的右区间所在结点的下标。
感觉代码有几个神奇之处:
1. root[i] = root[i-1]; 直接把 [1,i-1] 这课线段树上存储的东西 给了 [1,i] 这颗线段树 , 相当于第i颗线段树只需要特指 logn个结点来更新被 第i个数改变的结点,其他结点由于没有更新,所以可以沿用第i-1 颗线段树上的内容。
2. Tree[0].l=Tree[0].r=Tree[0].sum=root[0]=0; 创建了第0颗线段树,和第0个结点,这样所有的没有存储内容的结点都可以用第0个结点所表示。
3. void update(int x,int &fre,int l,int r)
Tree[++cnt] = Tree[fre];
fre = cnt ;
update(x,Tree[fre].l,l,m);
动态建点,给它分配下标,然后将下标带回
Tree[fre].l
代码:
#include<cstdio> #include<algorithm> #define maxn 100100 using namespace std; struct nn{ int value,id; }num[maxn]; bool cmp(nn x,nn y){ return x.value<y.value; } struct node{ int l,r,sum; }Tree[maxn*20]; int root[maxn]; int a[maxn],cnt; void update(int x,int &fre,int l,int r){ Tree[++cnt] = Tree[fre]; fre = cnt ; Tree[fre].sum++; if (l==r) return ; int m = (l+r)>>1; if (x<=m){ update(x,Tree[fre].l,l,m); }else{ update(x,Tree[fre].r,m+1,r); } } int query(int a,int b,int k,int l,int r){ if (l==r) return l; int num_sum = Tree[Tree[b].l].sum - Tree[Tree[a].l].sum; int m = (l+r)>>1; // printf("a=%d b=%d k=%d l=%d r=%d num_sum=%d m=%d \n",a,b,k,l,r,num_sum,m); if (num_sum<k){ return query(Tree[a].r,Tree[b].r,k-num_sum,m+1,r); }else{ return query(Tree[a].l,Tree[b].l,k,l,m); } } int main() { int n,m; scanf("%d%d",&n,&m); for (int i=1;i<=n;i++){ scanf("%d",&num[i].value); num[i].id = i; } sort(num+1,num+1+n,cmp); for (int i=1;i<=n;i++){ a[num[i].id] = i; } cnt=Tree[0].l=Tree[0].r=Tree[0].sum=root[0]=0; for (int i=1;i<=n;i++){ root[i] = root[i-1]; update(a[i],root[i],1,n); } // for (int i=1;i<=n;i++){ // printf("root[%d] = %d\n",i,root[i]); // } // for (int i=0;i<=cnt;i++){ // printf("i = %d, Tree[].l=%d, Tree[].r=%d ,Tree[].sum=%d\n",i,Tree[i].l,Tree[i].r,Tree[i].sum); // } for (int i=1;i<=m;i++){ int aa,bb,kk; scanf("%d%d%d",&aa,&bb,&kk); printf("%d\n",num[query(root[aa-1],root[bb],kk,1,n)].value); } return 0; }