划分树成熟模板(可解决相同元素)+二分
来源:互联网 发布:家用交换机推荐 知乎 编辑:程序博客网 时间:2024/04/27 15:42
HDOJ 4417 - Super Mario 划分树成熟模板(可解决相同元素)+二分
POJ 2104 - K-th Number 划分树初步(不能有相同元素)
分类: 划分树2013-08-07 19:25 150人阅读 评论(0) 收藏 举报 划分树的模型是用来解决一类求一列数的某个区间第k小数的问题.当然也可以通过二分来利用划分树求一个数在某区间里是第几小...跟我的感觉划分树和线段树很多地方挺相似的..
1、划分树的构造
划分树和线段树一样...采用隐性建树...建造的是二叉树...划分树通常使用一个二维数组存储: tree[t][n] ...第一维代表是树的第几层..第二维说明了这层n个数的排列情况..该层n个数虽然连续存储在一起...但可能隐形的分为了好几段.或者说属于不同的点...抽象的说..这些层数列记录的是一个快排的过程..每次快排的基准值是这一段的中位数(中间大的数)....每一层..还未有序的一块就是一个点...可见.第一层..n个数为一个点..第二层1~mid为一个点..mid+1~n为一个点..在这样下去...到了最后面一层..已经成为了一列非递减的数列...而对于每个节点(节点记录的是一排数)..其左右孩子就是这个节点代表的数列段以中位数为基准值后..小于基准值的点以及基准点按照原来的顺序放到排列好..作为其左子树..其他的作为右子树..为了方便后面的统计..构造时同时做一个预处理...记录每一层..到了每个点..其左侧有多少个点划入了左子树...
2、询问过程
询问是找[L,R]区间的第k小数...query函数的形式和线段树一样...那么从第一层进入划分树递归找...区间[L,R]有LtoR个数进入了左子树(建树时做了预处理,直接算出)..若LtoR>=k..那么答案肯定在其左子数上...否则就在其右子树上...但这里和线段树有区别了..线段树在带的时候[L,R]是不变的..这里要调整..因为这里的[L,R]随着层数的深入..是不断的通过映射关系改变的...具体调整方法..参考代码..
Program:
- #include<iostream>
- #include<stdio.h>
- #include<string.h>
- #include<cmath>
- #include<queue>
- #include<stack>
- #include<set>
- #include<map>
- #include<algorithm>
- #define ll long long
- #define eps 1e-5
- #define oo 1000000007
- #define pi acos(-1.0)
- #define MAXN 100005
- using namespace std;
- int tree[21][MAXN],num[21][MAXN],sorted[MAXN];
- void built(int l,int r,int t)
- {
- if (l==r) return;
- int i,x,y,mid=(r+l)>>1;
- x=l;y=mid+1; //左子树的起点x...右子树的起点y
- for (i=l;i<=r;i++)
- {
- num[t][i]=num[t][i-1]; //预处理每一层每个点其自己以及左侧到左子树的个数
- if (tree[t][i]<=sorted[mid]) tree[t+1][x++]=tree[t][i],num[t][i]++; // sorted[mid]为这一段的中位数,放入左子树
- else tree[t+1][y++]=tree[t][i]; //放入右子树
- }
- built(l,mid,t+1),built(mid+1,r,t+1); //递归建树
- }
- int query(int L,int R,int k,int l,int r,int t)
- {
- if (L==R) return tree[t][L];
- int ltoL,LtoR,mid=(l+r)>>1;
- ltoL=num[t][L-1]-num[t][l-1]; //ltoL [l,L]的到左子树点的个数
- LtoR=num[t][R]-num[t][L-1]; // LtoR [L,R]的到左子树点的个数
- if (LtoR>=k) return query(l+ltoL,l+ltoL+LtoR-1,k,l,mid,t+1); //往左子树去
- /* 找其在左子树的映射区间,l+ltoL这个区间到左子树的左边界,l+ltoL+LtoR-1右边界(-1是为了处理边界问题)
- (l,mid)为(l,r)左子树的左右点. */
- int b=L-l-ltoL,bb=R-L+1-LtoR; //求相应的到右子树点的个数
- return query(mid+b+1,mid+b+bb,k-LtoR,mid+1,r,t+1); //往右子树去
- /* k-LtoR...减去去了左子树的点.. */
- }
- int main()
- {
- int n,m,i;
- scanf("%d%d",&n,&m);
- for (i=1;i<=n;i++) scanf("%d",&tree[0][i]),sorted[i]=tree[0][i];
- memset(num,0,sizeof(num));
- sort(sorted+1,sorted+1+n);
- built(1,n,0);
- while (m--)
- {
- int l,r,k;
- scanf("%d%d%d",&l,&r,&k);
- printf("%d\n",query(l,r,k,1,n,0));
- }
- return 0;
- }
POJ 2104 - K-th Number 划分树初步(不能有相同元素)
划分树的模型是用来解决一类求一列数的某个区间第k小数的问题.当然也可以通过二分来利用划分树求一个数在某区间里是第几小...跟我的感觉划分树和线段树很多地方挺相似的..
1、划分树的构造
划分树和线段树一样...采用隐性建树...建造的是二叉树...划分树通常使用一个二维数组存储: tree[t][n] ...第一维代表是树的第几层..第二维说明了这层n个数的排列情况..该层n个数虽然连续存储在一起...但可能隐形的分为了好几段.或者说属于不同的点...抽象的说..这些层数列记录的是一个快排的过程..每次快排的基准值是这一段的中位数(中间大的数)....每一层..还未有序的一块就是一个点...可见.第一层..n个数为一个点..第二层1~mid为一个点..mid+1~n为一个点..在这样下去...到了最后面一层..已经成为了一列非递减的数列...而对于每个节点(节点记录的是一排数)..其左右孩子就是这个节点代表的数列段以中位数为基准值后..小于基准值的点以及基准点按照原来的顺序放到排列好..作为其左子树..其他的作为右子树..为了方便后面的统计..构造时同时做一个预处理...记录每一层..到了每个点..其左侧有多少个点划入了左子树...
2、询问过程
询问是找[L,R]区间的第k小数...query函数的形式和线段树一样...那么从第一层进入划分树递归找...区间[L,R]有LtoR个数进入了左子树(建树时做了预处理,直接算出)..若LtoR>=k..那么答案肯定在其左子数上...否则就在其右子树上...但这里和线段树有区别了..线段树在带的时候[L,R]是不变的..这里要调整..因为这里的[L,R]随着层数的深入..是不断的通过映射关系改变的...具体调整方法..参考代码..
Program:
- #include<iostream>
- #include<stdio.h>
- #include<string.h>
- #include<cmath>
- #include<queue>
- #include<stack>
- #include<set>
- #include<map>
- #include<algorithm>
- #define ll long long
- #define eps 1e-5
- #define oo 1000000007
- #define pi acos(-1.0)
- #define MAXN 100005
- using namespace std;
- int tree[21][MAXN],num[21][MAXN],sorted[MAXN];
- void built(int l,int r,int t)
- {
- if (l==r) return;
- int i,x,y,mid=(r+l)>>1;
- x=l;y=mid+1; //左子树的起点x...右子树的起点y
- for (i=l;i<=r;i++)
- {
- num[t][i]=num[t][i-1]; //预处理每一层每个点其自己以及左侧到左子树的个数
- if (tree[t][i]<=sorted[mid]) tree[t+1][x++]=tree[t][i],num[t][i]++; // sorted[mid]为这一段的中位数,放入左子树
- else tree[t+1][y++]=tree[t][i]; //放入右子树
- }
- built(l,mid,t+1),built(mid+1,r,t+1); //递归建树
- }
- int query(int L,int R,int k,int l,int r,int t)
- {
- if (L==R) return tree[t][L];
- int ltoL,LtoR,mid=(l+r)>>1;
- ltoL=num[t][L-1]-num[t][l-1]; //ltoL [l,L]的到左子树点的个数
- LtoR=num[t][R]-num[t][L-1]; // LtoR [L,R]的到左子树点的个数
- if (LtoR>=k) return query(l+ltoL,l+ltoL+LtoR-1,k,l,mid,t+1); //往左子树去
- /* 找其在左子树的映射区间,l+ltoL这个区间到左子树的左边界,l+ltoL+LtoR-1右边界(-1是为了处理边界问题)
- (l,mid)为(l,r)左子树的左右点. */
- int b=L-l-ltoL,bb=R-L+1-LtoR; //求相应的到右子树点的个数
- return query(mid+b+1,mid+b+bb,k-LtoR,mid+1,r,t+1); //往右子树去
- /* k-LtoR...减去去了左子树的点.. */
- }
- int main()
- {
- int n,m,i;
- scanf("%d%d",&n,&m);
- for (i=1;i<=n;i++) scanf("%d",&tree[0][i]),sorted[i]=tree[0][i];
- memset(num,0,sizeof(num));
- sort(sorted+1,sorted+1+n);
- built(1,n,0);
- while (m--)
- {
- int l,r,k;
- scanf("%d%d%d",&l,&r,&k);
- printf("%d\n",query(l,r,k,1,n,0));
- }
- return 0;
- }
题意:
给一列数..若干个询问..问(l,r,h)...在[l,r]范围内..有多少个数小于等于h....
题解:
二分第k小..用划分树 查找第k小是哪个数..就可以把每个提问的答案找出来了...
读入数据后..忘记要L++,R++了..浪费了好多时间..终于把划分树的最终模板敲定了...去年网络赛的时候..看到这题.就想到了kth number..马上去找了个kth number的代码..加个二分.就水过了...当时运气也好..万一找打那份代码不能解决相同元素就要跪烂了...真正要自己来写前面的划分树.确实没这么轻松...
今天队内赛的一题和这道一模一样...改了一下提交..居然超时了..应该是spoj的机器太挫了吧...看来还是得用线段树(树状数组)来离线处理了..
Program:
- #include<iostream>
- #include<stdio.h>
- #include<string.h>
- #include<cmath>
- #include<queue>
- #include<stack>
- #include<set>
- #include<map>
- #include<algorithm>
- #define ll long long
- #define eps 1e-5
- #define oo 1000000007
- #define pi acos(-1.0)
- #define MAXN 100005
- using namespace std;
- int tree[21][MAXN],num[21][MAXN],sorted[MAXN];
- void built(int l,int r,int t)
- {
- if (l==r) return;
- int i,x,y,mid=r+l>>1,m=mid-l+1;
- x=l,y=mid+1;
- for (i=l;i<=r;i++)
- if (sorted[i]<sorted[mid]) m--;
- for (i=l;i<=r;i++)
- {
- num[t][i]=num[t][i-1];
- if (tree[t][i]==sorted[mid])
- {
- if (m) tree[t+1][x++]=tree[t][i],num[t][i]++,m--;
- else tree[t+1][y++]=tree[t][i];
- }else
- if (tree[t][i]<sorted[mid]) tree[t+1][x++]=tree[t][i],num[t][i]++;
- else tree[t+1][y++]=tree[t][i];
- }
- built(l,mid,t+1),built(mid+1,r,t+1);
- }
- int query(int L,int R,int k,int l,int r,int t)
- {
- if (L==R) return tree[t][L];
- int ltoL,LtoR,mid=l+r>>1;
- ltoL=num[t][L-1]-num[t][l-1],LtoR=num[t][R]-num[t][L-1];
- if (LtoR>=k) return query(l+ltoL,l+ltoL+LtoR-1,k,l,mid,t+1);
- int b=L-l-ltoL,bb=R-L+1-LtoR;
- return query(mid+b+1,mid+b+bb,k-LtoR,mid+1,r,t+1);
- }
- int main()
- {
- int T,cases,i,n,m;
- scanf("%d",&T);
- for (cases=1;cases<=T;cases++)
- {
- scanf("%d%d",&n,&m);
- memset(num,0,sizeof(num));
- for (i=1;i<=n;i++) scanf("%d",&tree[0][i]),sorted[i]=tree[0][i];
- sort(sorted+1,sorted+1+n);
- built(1,n,0);
- printf("Case %d:\n",cases);
- while (m--)
- {
- int L,R,H,l,r,mid;
- scanf("%d%d%d",&L,&R,&H);
- L++,R++;
- l=0,r=R-L+2;
- while (r-l>1)
- {
- mid=r+l>>1;
- if (query(L,R,mid,1,n,0)<=H) l=mid;
- else r=mid;
- }
- printf("%d\n",l);
- }
- }
- return 0;
- }
- 划分树成熟模板(可解决相同元素)+二分
- HDOJ 4417 - Super Mario 划分树成熟模板(可解决相同元素)+二分
- hdu4417Super Mario【划分树+二分模板】
- 连续元素划分,二分答案
- POJ 2104 - K-th Number 划分树初步(不能有相同元素)
- hdu3727Jewel(划分树+二分)
- 划分树模板
- 划分树模板
- 划分树模板
- 划分树 模板
- 划分树模板题
- 划分树模板题
- 划分树模板
- 划分树模板
- 划分树模板
- 划分树模板
- 划分树 模板
- 划分树算法 模板
- freemarker的list指令中隐藏的变量
- java操作xml
- hdu 1023 - Train Problem II
- 心看来已不在霆锋身上
- Java网页抓取错误总结
- 划分树成熟模板(可解决相同元素)+二分
- DBGridEh使用UniDac默认不能排序和过滤的解决方案
- 一道题弄懂宽度优先搜索 Breadth first search
- UNIX网络编程--I/O复用:select函数和poll函数讲解(六)
- 软考总结
- 帝国cms列表页模板中标题的截取与内容的截取
- 黑马程序员 java入门:数组
- 运用递归给定一个int型的整数,倒序输出他的每一位上的数字
- 注册表修复