HDU 4417 - Super Mario ( 划分树+二分 / 树状数组+离线处理+离散化)
题意:给一个数组,每次询问输出在区间[L,R]之间小于H的数字的个数。
此题可以使用划分树在线解决。
划分树可以快速查询区间第K小个数字。逆向思考,判断小于H的最大的一个数字是区间第几小数,即是答案。这一步可以使用二分搜索上界。时间复杂度是O(logn*logn)。
#include <iostream> #include <cstdio> #include <cstring> #include <stack> #include <algorithm> #define MAXN 100005 using namespace std; struct Divide_Tree { ][MAXN]; ][MAXN]; void build(int c,int L,int R) { ,lsame=mid+-L,lp=L,rp=mid+; for(int i=L; i<mid; ++i) if(sorted[i]<sorted[mid]) lsame--; for(int i=L; i<=R; ++i) { ; ]; if(dat[c][i]<sorted[mid]) { dat[c+][lp++]=dat[c][i]; toleft[c][i]++; } else if(dat[c][i]>sorted[mid]) dat[c+][rp++]=dat[c][i]; else { if(lsame) { lsame--; toleft[c][i]++; dat[c+][lp++]=sorted[mid]; } ][rp++]=sorted[mid]; } } if(L==R) return ; build(c+,L,mid); build(c+,mid+,R); } int query(int c,int L,int R,int ql,int qr,int k) { if(L==R) return dat[c][L]; ; int la,lb,ra,rb; ; ]; lb=toleft[c][qr]; ra=ql-L-la; rb=qr+-L-lb; int s=lb-la; ,L,mid,L+la,L+lb-,k); ,mid+,R,mid++ra,mid+rb,k-s); } }; Divide_Tree tree; int n; int Bsearch(int low,int high,int key,int ql,int qr) { )>>; while(low<high) { ,,n,ql,qr,mid)<=key) low=mid; ; mid=(low+high+)>>; } ,,n,ql,qr,mid)<=key) return mid; ; } int main() { ; int q; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&q); ; i<=n; ++i) { scanf("%d",&tree.arr[i]); tree.sorted[i]=tree.dat[][i]=tree.arr[i]; } sort(tree.sorted+,tree.sorted+n+); tree.build(,,n); int l,r,k; printf("Case %d:\n",++kase); while(q--) { scanf("%d%d%d",&l,&r,&k); l++; r++; ,r-l+,k,l,r); ) printf("0\n"); else printf("%d\n",p); } } ; }
另外,此题有更高效的算法。 未完待续。
此题也可以离线解决。
首先考虑单次查询整个区间小于某个数的数字个数的思路,我们可以统计每个数字出现的次数,然后利用前缀和快速计算小于该数的数字个数。
如果是查询部分区间[L,R]小于某数x的数字个数的话,答案为 区间[1,R]小于x的数字个数 减去 区间[1,L-1]小于x的数字个数。那么如何计算区间[1,S]内小于x的数字个数呢。
每出现一个数字v就在第v个位置加一即可。统计小于x的数字个数即计算前x个位置的和,这里需要求和,也用到了修改,显然用树状数组更高效。这样当枚举到第i个数字时,求区间[1,i]内小于x的数字个数即此时计算前x个数字的和。
由于这里数字较大,数组下标存不下,所以需要离散化。
#include<iostream> #include<vector> #include<cstring> #include<map> #include<cstdio> #include<algorithm> #define MAXN 100005 using namespace std; int n,m,cn; map<int,int> has; struct BIT { int dat[MAXN]; void clear() { memset(dat,,sizeof(dat)); } int lowbit(int x) { return -x&x; } void add(int x,int v) { while(x<=cn) { dat[x]+=v; x+=lowbit(x); } } int sum(int x) { ; ) { s+=dat[x]; x-=lowbit(x); } return s; } }; struct Segment { int num,left,right,high; int presum,ans; Segment (int a,int b,int c,int d):num(a),left(b),right(c),high(d) {} bool operator < (const Segment &p) const { return left<p.left; } }; bool cmp(Segment a,Segment b) { return a.num<b.num; } vector<Segment> vec; vector<int> numb; int arr[MAXN]; vector<int> posL[MAXN],posR[MAXN]; BIT tree; int main() { int T; scanf("%d",&T); ; while(T--) { scanf("%d%d",&n,&m); numb.clear(); ; i<=n; ++i) { scanf("%d",&arr[i]); numb.push_back(arr[i]); posL[i+].clear(); posR[i+].clear(); } vec.clear(); ; i<m; ++i) { int x,y,z; scanf("%d%d%d",&x,&y,&z); x++; y++; numb.push_back(z); vec.push_back(Segment (i,x,y,z)); } sort(vec.begin(),vec.end()); ; i<vec.size(); ++i) { posL[vec[i].left].push_back(i); posR[vec[i].right].push_back(i); } has.clear(); cn=; sort(numb.begin(),numb.end()); ; i<numb.size(); ++i) if(!has[numb[i]]) has[numb[i]]=++cn; tree.clear(); ; i<=n; ++i) { ; j<posL[i].size(); ++j) { int u=posL[i][j]; int v=has[vec[u].high]; vec[u].presum=tree.sum(v); } tree.add(has[arr[i]],); ; j<posR[i].size(); ++j) { int u=posR[i][j]; int v=has[vec[u].high]; vec[u].ans=tree.sum(v)-vec[u].presum; } } sort(vec.begin(),vec.end(),cmp); printf("Case %d:\n",++kase); ; i<vec.size(); ++i) printf("%d\n",vec[i].ans); } ; }
离线的另一种思路。
我们可以按照每个数的大小顺序插入到树状数组中,同时按照高度的大小顺序查询。
这样将所有数和高度一起存入数组并从小到大排序。这样遇到数就在树状数组该数字的位置加一,遇到查询就对该区间求和,这样可以保证在查询的时候树状数组上被插入的数都是小于x的。
注意,排序的时候如果数的大小和查询高度大小一样,则查询放在后面。
#include<iostream> #include<vector> #include<cstring> #include<map> #include<cstdio> #include<algorithm> #define MAXN 100005 using namespace std; int n,m; struct BIT { int dat[MAXN]; void clear() { memset(dat,,sizeof(dat)); } int lowbit(int x) { return -x&x; } void add(int x,int v) { while(x<=n) { dat[x]+=v; x+=lowbit(x); } } int sum(int x) { ; ) { s+=dat[x]; x-=lowbit(x); } return s; } }; struct Segment { int num,dat,left,right; int ans; Segment(,,,):num(a),dat(b),left(c),right(d) {} bool operator <(const Segment &p) const { return dat<p.dat||(dat==p.dat&&num>p.num); } }; bool cmp(Segment a,Segment b) { return a.num<b.num; } vector<Segment> vec; BIT tree; int main() { ; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); vec.clear(); ; i<=n; ++i) { int t; scanf("%d",&t); vec.push_back(Segment(i,t)); } ; i<m; ++i) { int x,y,z; scanf("%d%d%d",&x,&y,&z); x++; y++; vec.push_back(Segment(-m+i,z,x,y)); } sort(vec.begin(),vec.end()); tree.clear(); ; i<vec.size(); ++i) { ) tree.add(vec[i].num,); ); } sort(vec.begin(),vec.end(),cmp); printf("Case %d:\n",++kase); ; i<vec.size(); ++i) ) printf("%d\n",vec[i].ans); else break; } ; }
HDU 4417 - Super Mario ( 划分树+二分 / 树状数组+离线处理+离散化)的更多相关文章
- HDU 4417 Super Mario(划分树)
Super Mario Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- HDU 4417 Super Mario(划分树问题求不大于k的数有多少)
Super Mario Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- HDU 4417 Super Mario(划分树+二分)
题目链接 #include <cstdio> #include <cstring> #include <algorithm> using namespace std ...
- HDU 4417 Super Mario(2012杭州网络赛 H 离线线段树)
突然想到的节约时间的方法,感觉6翻了 给你n个数字,接着m个询问.每次问你一段区间内不大于某个数字(不一定是给你的数字)的个数 直接线段树没法做,因为每次给你的数字不一样,父节点无法统计.但是离线一 ...
- HDU 4417 Super Mario (划分树)(二分)
Super Mario Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- HDU 4417 Super Mario(主席树求区间内的区间查询+离散化)
Super Mario Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- hdu 4417 Super Mario/树套树
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 题意很简单,给定一个序列求一个区间 [L, R,]中小于等于H的元素的个数. 好像函数式线段树可 ...
- 主席树:HDU 4417 Super Mario
Super Mario Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- hdu 4417 Super Mario 树状数组||主席树
Super Mario Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Prob ...
随机推荐
- OA系统部门结构树
public class DepartmentUtils { /** * @param topList 顶级部门列表 * @param removeId 删除部门的id * @return */ pu ...
- mac 下基于firebreath 开发多浏览器支持的浏览器插件
mac 下基于firebreath 开发多浏览器支持的浏览器插件 首先要区分什么是浏览器扩展和浏览器插件;插件可以像本地程序一样做的更多 一. 关于 firebreath http://www.fir ...
- CAS原理全面分析
http://blog.chinaunix.net/uid-22816738-id-3525939.html 上文对CAS各方面原理做了很详细.很明了分析,包括CAS架构.认证协议.安全性.登录.认证 ...
- GRUB4DOS入门
目 录 第1章 GRUB4DOS入门 1 1.1 用途 1 1.2 安装 1 1.2.1 修改MBR 1 1.2.2 修改PBR 3 1.2.3 DOS 4 1.2 ...
- table合并单元格colspan和rowspan .
colspan和rowspan这两个属性用于创建特殊的表格. colspan是“column span(跨列)”的缩写.colspan属性用在td标签中,用来指定单元格横向跨越的列数: 在浏览器中将显 ...
- Java并发编程:并发容器之ConcurrentHashMap
转载: Java并发编程:并发容器之ConcurrentHashMap JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的 ...
- robotframework笔记5
循环条件 *** Settings *** Library BuiltIn Library Collections *** Test Cases *** TestCase01 My Keyword 0 ...
- C++的vector学习abc
开始学习和使用vector了,用到之后再去学似乎神迹的感觉啊,就像跑一下就能给个糖吃哈哈 百度上的六种初始化的方法就不再说了,那些方法都很对. 如果没有值初始化,系统会自行初始化,目前我遇到的是用脚标 ...
- SQL语句技巧(上个样式太差了)
以下并非本人整理,但是看后感觉相当不错,特此分享. 1.应用程序中,保证在实现功能的基础上,尽量减少对数据库的访问次数:通过搜索参数,尽量减少对表的访问行数,最小化结果集,从而减轻网络负担:能够分 ...
- boost 源码编译 的 Makefile.am写法备份
include $(top_srcdir)/common.mk bin_PROGRAMS= lib_LIBRARIES= lib_LTLIBRARIES= lib_LTLIBRARIES+=libSt ...