pku 2104划分树
2012-08-25 20:41
288 查看
之前未做过划分树的题,今日撞上,得探个究竟;从网上找资料看看,觉得HH大牛上的博客写得不错
附上网址:http://www.notonlysuccess.com/index.php/divide-tree/
这里我简单介绍下它的入门,都知道它在求序列区间上的第K最大值有log(n)的算法,不过它所求的是离线的,所谓离线就是数据给定后就不会进行对数据进行改变!
划分树主要的思想就是快排,线段树只是被当作工具利用而已!整体方法如下:对输入的数据副本进行排序,这样对应线段树区间的中位数就知道是多少了,也就是说可以知道线段树区间里的元素是往左放还是往右放,划分树的建立通常要在线段树建立上再加一个参数:层数d,因为要对每层的划分结果进行保存;
给定 划分树层结果保存数组 var[d]
第0层的数据已经输入;数据排序数组sorted
; 对每层d对应的各个区间【L,r]里对每个 L<=i<=r ,let[d][i] 表示区间里【l,i]里有多少个元素要放到左子树(该区间里的元素是对这一层的数据,记录这个是为查找的时候进行区间更新!) 以及线段树T[4*N];
建立函数: void build(int left,int right,int d,int k)
{
当left == right 返回
mid = 区间中点
(如果给定的数据有重复元素的话,我们也就需要知道跟此区间中点的元素相同的有多少个要放到左子树,有多少个放到)
左子树应该放(mid-l+1)个元素,少于sorted[mid]的元素有a个(a 是对区间元素遍历得来的),
那么还有 mid-l+1-a个元素是放sorted[mid];
for i=l to r
先赋值let[d][i] = let[d][i-1] ,(后续计算中只需要知道第i个元素是放左子树还是右子树就可对let[d][i]进行更新)
下标为i的元素是放左子树还是放右子树,通过与sorted[mid]比较则可得到,
当然遇到等于sorted[mid]的元素要看左子树是否还能放它
放到左子树的元素要使let[d][i] ++;
end
递归建立下一层!(左子树和右子树)
}
查找区间【l,r】的最第K最大值: int find(l,r,K,d,k)
{
l == r return var[d][l];
看这个区间有多少人元素放左子树 lnum 有多少元素放右子树rnum
看区间【T[k].l,l-1】有多少元素放左子树llnum,有多少元素放右子树rrnum
如果lnum>=k 说明要找的元素在左子树,我们应该在哪个区间去找它的第K大值,
显然一般不是整个左子树,因为还有不属于区间【l,r]的元素也放到了左子树,
但我们会发现一点的是不属于区间【l,r】的元素要么排在左子树的前面,
要么排在左子树的后面,而中间的区间 则是我们要查找的区间,
显然简单计算就知道为【 T[k].l+llnum ,T[k].l+llnum+lnum-1】
同理如果要在右子树中查找的话,对应的区间应该是【T[k].mid + rrnum,T[k].mid+rrnum+rnum-1]
}
附代码:
附上网址:http://www.notonlysuccess.com/index.php/divide-tree/
这里我简单介绍下它的入门,都知道它在求序列区间上的第K最大值有log(n)的算法,不过它所求的是离线的,所谓离线就是数据给定后就不会进行对数据进行改变!
划分树主要的思想就是快排,线段树只是被当作工具利用而已!整体方法如下:对输入的数据副本进行排序,这样对应线段树区间的中位数就知道是多少了,也就是说可以知道线段树区间里的元素是往左放还是往右放,划分树的建立通常要在线段树建立上再加一个参数:层数d,因为要对每层的划分结果进行保存;
给定 划分树层结果保存数组 var[d]
第0层的数据已经输入;数据排序数组sorted
; 对每层d对应的各个区间【L,r]里对每个 L<=i<=r ,let[d][i] 表示区间里【l,i]里有多少个元素要放到左子树(该区间里的元素是对这一层的数据,记录这个是为查找的时候进行区间更新!) 以及线段树T[4*N];
建立函数: void build(int left,int right,int d,int k)
{
当left == right 返回
mid = 区间中点
(如果给定的数据有重复元素的话,我们也就需要知道跟此区间中点的元素相同的有多少个要放到左子树,有多少个放到)
左子树应该放(mid-l+1)个元素,少于sorted[mid]的元素有a个(a 是对区间元素遍历得来的),
那么还有 mid-l+1-a个元素是放sorted[mid];
for i=l to r
先赋值let[d][i] = let[d][i-1] ,(后续计算中只需要知道第i个元素是放左子树还是右子树就可对let[d][i]进行更新)
下标为i的元素是放左子树还是放右子树,通过与sorted[mid]比较则可得到,
当然遇到等于sorted[mid]的元素要看左子树是否还能放它
放到左子树的元素要使let[d][i] ++;
end
递归建立下一层!(左子树和右子树)
}
查找区间【l,r】的最第K最大值: int find(l,r,K,d,k)
{
l == r return var[d][l];
看这个区间有多少人元素放左子树 lnum 有多少元素放右子树rnum
看区间【T[k].l,l-1】有多少元素放左子树llnum,有多少元素放右子树rrnum
如果lnum>=k 说明要找的元素在左子树,我们应该在哪个区间去找它的第K大值,
显然一般不是整个左子树,因为还有不属于区间【l,r]的元素也放到了左子树,
但我们会发现一点的是不属于区间【l,r】的元素要么排在左子树的前面,
要么排在左子树的后面,而中间的区间 则是我们要查找的区间,
显然简单计算就知道为【 T[k].l+llnum ,T[k].l+llnum+lnum-1】
同理如果要在右子树中查找的话,对应的区间应该是【T[k].mid + rrnum,T[k].mid+rrnum+rnum-1]
}
附代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <map> #include <set> #include <string> #include <cstring> #include <list> #include <queue> #include <stack> #include <cmath> using namespace std; #define PF(x) (scanf("%d",&x)) #define PT(x,y) (scanf("%d%d",&x,&y)) #define PR(x) (printf("%d\n",x)) #define PRT(x,y)(printf("%d %d\n",x,y)) #define PB(x)(scanf("%I64d",&x)) #define PRB(x)(printf("%I64d\n",(x))) #define For(i,n) for(int i=0;i<(n);i++) #define CLR(ar) (memset(ar,0,sizeof(ar))) #define CLR1(ar) (memset(ar,-1,sizeof(ar))) #define Max(x,y) (x)>(y)?(x):(y) #define Min(x,y) (x)>(y)?(y):(x) #define L(x) (x<<1) #define R(x) ((x<<1)|1) #define Mid(x,y) ((x+y)>>1) typedef __int64 LL; #define N 100005 #define M 105 #define Mod 1000 #define Inf 0x7fffffff struct tree { int l,r; int m() { return (l+r)>>1; } }; tree T[4*N]; int var[30] ; int let[30] ; int sorted ; int n,m; void build(int l,int r,int d,int k) { T[k].l = l; T[k].r = r; if(l == r) {return ;} int mid = T[k].m(); int samemid = mid-l+1; for(int i=l;i<=r;i++) { if(var[d][i]<sorted[mid]) samemid--; } int lbound = l; int rbound = mid+1; int same = 0; for(int i=l;i<=r;i++) { if(i == l) { let[d][i] = 0; } else let[d][i] = let[d][i-1]; if(var[d][i]<sorted[mid]) { let[d][i]++; var[d+1][lbound++] = var[d][i]; } else if(var[d][i]>sorted[mid]) { var[d+1][rbound++] = var[d][i]; } else { if(same<samemid) { var[d+1][lbound++] = var[d][i];same++; let[d][i]++; } else var[d+1][rbound++] = var[d][i]; } } build(l,mid,d+1,L(k)); build(mid+1,r,d+1,R(k)); } int finds(int l,int r,int n_k,int d,int k) { if(T[k].l == T[k].r ) return var[d][T[k].l]; int lnum,rnum; if(l == T[k].l) { lnum = 0; rnum = let[d][r]; } else { lnum = let[d][l-1]; rnum = let[d][r] - let[d][l-1]; } if(rnum>=n_k) { int ll,rr; ll = T[k].l + lnum; rr = T[k].l + lnum+rnum-1; return finds(ll,rr,n_k,d+1,L(k)); } else { int ll,rr; int llnum,rrnum; llnum = l-T[k].l-lnum; rrnum = r-l+1-rnum; ll = T[k].m()+llnum+1; rr = T[k].m()+llnum+rrnum; return finds(ll,rr,n_k-rnum,d+1,R(k)); } } void init() { while(scanf("%d%d",&n,&m)!=EOF) { For(i,n) {PF(var[0][i+1]);sorted[i+1] = var[0][i+1];} sort(sorted+1,sorted+n+1); build(1,n,0,1); For(i,m) { int l,r,n_k; PT(l,r);PF(n_k); PR(finds(l,r,n_k,0,1)); } } return ; } int main() { init(); return 0; }
相关文章推荐
- POJ2104 K-th Number——划分树——pku2104
- POJ 2104 K-th Number (划分树)
- [PKU 2104]K-th Number(树套树版)
- PKU 2104(线段树+归并+二分)
- pku2104 第k大数-划分树做法
- 区间第K大数——划分树(POJ2104解题报告)
- poj 2104 K-th Number(划分树)
- POJ 2104 K-th Number(划分树)
- POJ 2104 K-th Number(区间第k大数)(平方分割,归并树,划分树)
- POJ 2104-K-th Number(划分树)求区间内第k小的数
- poj 2104 K-th Number (划分树模板题)
- poj 2104 K-th Number (划分树)
- hdu 2665 (poj 2104) 划分树
- POJ 2104 划分树模板题
- 划分树 例题:POJ-2104/HDU-2665
- POJ-2104:K-th Number(划分树)
- poj2104——K-th Number(划分树)
- poj 2104 and hdu 2665 划分树模板入门题
- pku2104
- PKU-2104-K-th Number