树状数组(三)
来源:互联网 发布:日本网络电视直播apk 编辑:程序博客网 时间:2024/05/16 03:06
题目链接:codeforces 220B
题意分析:有n个数,m次询问,一开始给你这n个数ai,每一次询问的时候给你一个区间,求这个区间里面满足“数值为x,出现了x次”的条件的数有几个!(1 ≤ n, m ≤ 100000) 从题述上看,这题的难点应该是区间的频繁访问,于是可以想到线段树或者树状数组
解题思路:
即便是线段树或者树状数组这种高端数据结构,有时候也是需要一些辅助算法的,在这道题里面,辅助算法(思想)就是离线思想!
离线思想:其实很简单的一种思想,就是回应查询的时候不是一次查询一次回复,而是多次查询一次回复。那么这种思想究竟对我们解题有什么帮助呢?
我们来考虑用树状数组怎么解决这道题,首先,找到的数x,一定满足 x ≤ n。假设我们要查询一个区间[ l , r ],那么是不是如果我们事先知道了区间上点 i 到 r 这个区间有多少个数,就会方便很多?好像是会方便很多,但是好像会重复计算,那么我们就考虑这个点 i 在区间内是否是满足要求的数 x 。好像有点头绪了~
首先定义一个数组x,还有bit数组,是x的树状数组,另外我们需要一个vector(动态数组),vec[i]记录数字 i 在什么位置出现过:
从头开始遍历数组a,如果ai ≤ n则将其push到vec[ai]中,此时我们来检测一下vec[ai]的大小:
1)如果size ≥ ai,说明出现了一个满足题意的区间了,我们需要标记!于是,执行操作:x[ vec[ ai ][ size-ai ] ] = 1,表示从vec[ai][size-ai]那里到当前的位置 i,ai这个数满足条件。
2)如果size ≥ ai+1,说明我们之前已经将vec[ai][size-ai-1]这个位置标记了,这个点到当前位置 i 明显是有ai+1个ai的,我们需要订正它——x[ vec[ ai ][ size-ai ] ] = -1。(之所以是-1,是因为在[ vec[ai][size-ai-1] , i ]这个区间中,ai不是满足条件的数x了!)
3)如果size ≥ ai+2,我们又要把刚刚订正的地方修正回来——x[ vec[ ai ][ size-ai ] ] = 0。
因为,我们每次考虑问题都是以当前的 位置 i 为右界,那么为了节省时间,我们就应该对查询从左到右处理,在一切开始之前对查询按右边界进行排序。这就是所谓的离线处理。
每一次处理完一个数 i 之后,看看它是不是查询的右边界,是的话就用树状数组求和一下。
然后更新信息部分:
int tmp=a[i],len=vec[tmp].size()+1;vec[tmp].push_back(i);if(len>=tmp){ add(vec[tmp][len-tmp],1); if(len>tmp) add(vec[tmp][len-tmp-1],-2); if(len>tmp+1) add(vec[tmp][len-tmp-2],1);}
最后,按查询给出的顺序输出查询结果就行了!
AC代码:
#include <vector>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define INF 0x3f3f3f3fusing namespace std;int n,m,a[100005],bit[100005],ans[100005];vector<int> vec[100005];struct node{ int l,r,index;}q[100005];bool cmp(node x,node y){ return x.r<y.r;}int lowbit(int num){ return num&(-num);}int sum(int index){ int res=0; for(int i=index;i>0;i-=lowbit(i)) res+=bit[i]; return res;}void add(int index,int delta){ for(int i=index;i<=n;i+=lowbit(i)) bit[i]+=delta;}int main(){ while(~scanf("%d %d",&n,&m)) { memset(bit,0,sizeof(bit)); memset(vec,0,sizeof(vec)); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=0;i<m;i++) { scanf("%d %d",&q[i].l,&q[i].r); q[i].index=i; } sort(q,q+m,cmp); int j=0; for(int i=1;i<=n;i++) { if(a[i]<=n) { int tmp=a[i],len=vec[tmp].size()+1; vec[tmp].push_back(i); if(len>=tmp) { add(vec[tmp][len-tmp],1); if(len>tmp) add(vec[tmp][len-tmp-1],-2); if(len>tmp+1) add(vec[tmp][len-tmp-2],1); } } while(j<m&&q[j].r==i) { ans[q[j].index]=sum(q[j].r)-sum(q[j].l-1); j++; } } for(int i=0;i<m;i++) printf("%d\n",ans[i]); } return 0;}
总结:
1、离线思想,灵活运用可以提高算法复杂度;
2、树状数组是辅助工具,用来快速求和的!
- 树状数组(三)
- 树状数组专题(三)POJ2481
- 【模板篇】树状数组们(三)
- 树状数组三种写法
- NYOJ 士兵杀敌(三)(线段树,树状数组)
- (转)树状数组
- 树状数组(转载)
- Stars(树状数组)
- 树状数组(interval)
- (转)树状数组
- 树状数组(2)
- 树状数组(3)
- 树状数组(4)
- 树状数组(5)
- 树状数组(6)
- poj2352Stars(树状数组)
- POJ2299(树状数组)
- hdu2492 (树状数组)
- 大一第二学期第5周【程序阅读(3)】
- hibernate中关于数据库方言导致不能自动生成数据库表的问题
- 第五周项目3 多文件组织多个类
- 优秀开源代码解读之JS与iOS Native Code互调的优雅实现方案
- 对象作为数据成员-三角形类
- 树状数组(三)
- 第五轴上机项目4静态成员应用
- 第五周 项目3-用多文件组织多个类的程序
- Mac环境下svn的使用
- 大一第二学期第5周【程序阅读(4)】
- 第五周 阅读程序(3)
- 第五周阅读程序三
- Windows7下安装Ubuntu双系统
- 互联网人才真的抢疯了,大家先冷静一下?