题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417

题目意思给出一个序列,叫我们求一个区间里面小于等于k的数字个数。

这里面我用分块和主席树两种方法都做了一遍,恩,主席树虽然费空间,但是还是比分块块很多的。

因为每个人的风格不一样,所以我的代码可能比较长,比较繁琐。

首先是分块,分块的思想就是把整个区间划分成多个块,用数组来记录每个块的信息,当我们对一个区间进行查询或者修改的时候,一般来说就会有一些块完全的在这个区间里面,对于这种块被区间完全包含的情况,我们就可以对这些块进行整体的操作,把每一块看成一个整体来进行查询或者修改,而对于那种不完整的块(这种块一般就在区间的两头,最多就只有两块是没有完全被包含在区间里面的,其实左右两边的块就算是完整的我们也把它看成不完整的块来处理),我们对它进行暴力修改或者查询。

在这道题目里面,我们把给出的初始序列分块,假设我用a数组来储存,那么我把a数组复制给b数组,对b数组的每一个块进行块内排序,这样当我们查询区间小于等于k的的数字个数时,对于完整的块我们就可以在b数组里面用二分在块内进行查找(b数组是有序的),对于不完整的块,我们将在原来的数组a里面用暴力查找。

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque>
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 100005
int n,m,t,num;
int a[maxn],b[maxn];
int block,zu[maxn];//zu[i]表示第i个数字在第几块
void init(){
block=(int)sqrt(n);
num=-;
for(int i=;i<=n;i++){
zu[i]=(i-)/block+;
num=max(num,zu[i]);
}
}
int ask(int l,int r,int k){
int ans=;
for(int i=l;i<=min(r,zu[l]*block);i++){//左边不完整的块暴力查找
if(a[i]<=k)
ans++;
}
for(int i=zu[l]+;i<=zu[r]-;i++){//中间完整的块二分查找
int L=(i-)*block+;//块内左边界
int R=i*block; //块内右边界
ans+=upper_bound(b+L,b+R+,k)-(b+L);//找块内第一个大于k的数字
}
if(zu[l]!=zu[r]){//判断左边不完整的块和右边不完整的块是不是同一个块
for(int i=(zu[r]-)*block+;i<=r;i++){
if(a[i]<=k)
ans++;
}
}
return ans;
}
int main()
{
int Case=;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
init();
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
for(int i=;i<=num;i++){
int l=block*(i-)+;//当前块的左边界
int r=min(block*i,n);//当前块的右边界
sort(b+l,b+r+);//块内排序
}
printf("Case %d:\n",++Case);
while(m--){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",ask(l+,r+,k));//下标加一,因为我是从下标1开始的
}
}
return ;
}

然后就是主席树,我也是刚刚学,如果不会可以看这篇博客:

https://blog.csdn.net/bestFy/article/details/78650360

主席树可以用来求区间第k大,也可以用来求区间里面小于等于k的数字个数(发现新姿势),我的思路可能绕了圈圈,我们对给出的序列进行离散化,这样就得到了序列里面每个数字的相对大小(我们对每一个数字进行编号,编号为1的数字就是区间里面最大的数字),然后在建n课线段树,线段树的每一个节点代表着序列里面每一个数字的编号,从左往右编号依次增加,也就是说从左往右依次是第1大,第二大......第n大的数字;第 i 颗线段树就代表着原始序列里从1到i这段前缀中所有编号出现的情况(每一个编号的数字在这一段编号里面出现过没,出现了几次),这样我们建成的线段树就可以相互之间进行相减,和前缀和差不多,第5颗线段树的每一个节点减去第2颗线段树的每一个节点就是[3,5]这个区间的信息。可能不清楚,看上面博客好吧。

求区间比k大的数字的个数,我们可以在排序之后的序列里面找小于等于k的最后一个数字对应的位置,假设是index,那么线段树叶子中从1到index里所有叶子节点所对应的数字就是全部小于等于k了,这里面的都是相对大小,假如给出的区间是L和R,那么我们最后查询时就是查询第R颗线段树减去第L颗线段树,得到的一棵虚拟的数里面在区间[1,index]里面的和。

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque>
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 100005
struct node{
int l,r,sum;
}tree[maxn*];
int n,m,k,t,cnt;
struct point{
int id,w;
}a[maxn];
int b[maxn],rt[maxn];
bool operator <(point s1,point s2){
if(s1.w!=s2.w)
return s1.w<s2.w;
else
return s1.id<s2.id;
}
void update(int root){
tree[root].sum=tree[tree[root].l].sum+tree[tree[root].r].sum;
}
void build_0(int &root,int l,int r){
root=++cnt;
tree[root].l=l;
tree[root].r=r;
tree[root].sum=;
if(l==r)
return;
int mid=(l+r)/;
build_0(tree[root].l,l,mid);
build_0(tree[root].r,mid+,r);
update(root);
}
void build(int pre,int &root,int l,int r,int index){
root=++cnt;
tree[root]=tree[pre];
if(l==r){
tree[root].sum++;
return;
}
int mid=(l+r)/;
if(index<=mid)
build(tree[pre].l,tree[root].l,l,mid,index);
else
build(tree[pre].r,tree[root].r,mid+,r,index);
update(root);
}
int binary(int l,int r,int k){//二分查找区间里面小于等于k的最后一个数字所在的位置
while(l<=r){
int mid=(l+r)/;
if(a[mid].w>k)
r=mid-;
else
l=mid+;
}
return r;
}
int ask(int root1,int root2,int L,int R,int l,int r){
if(L>R)
return ;
if(l>=L&&r<=R){
return tree[root2].sum-tree[root1].sum;
}
int mid=(l+r)/;
int ans=;
if(mid>=L)
ans+=ask(tree[root1].l,tree[root2].l,L,R,l,mid);
if(mid<R)
ans+=ask(tree[root1].r,tree[root2].r,L,R,mid+,r);
return ans;
}
int main()
{
scanf("%d",&t);
int Case=;
while(t--){
scanf("%d%d",&n,&m);
cnt=;
for(int i=;i<=n;i++){
scanf("%d",&a[i].w);
a[i].id=i;
}
//离散化,我的离散化结果是没有重复的编号,这里其实有点多余
sort(a+,a+n+);
for(int i=;i<=n;i++){
b[a[i].id]=i;
}
build_0(rt[],,n);//建第0颗树
for(int i=;i<=n;i++)
build(rt[i-],rt[i],,n,b[i]);//建第i颗树
int l,r,k;
printf("Case %d:\n",++Case);
while(m--){
scanf("%d%d%d",&l,&r,&k);
l++;
r++;
int index=binary(,n,k);//查找序列里面最后一个小于等于k的数字的位置,对应于线段树的节点位置,因为都是排序之后的相对大小
printf("%d\n",ask(rt[l-],rt[r],,index,,n));
}
}
return ;
}

区间求小于等于k的数字个数 hdu4177的更多相关文章

  1. 面试题:m个长度为n的ordered array,求top k 个 数字

    package com.sinaWeibo.interview; import java.util.Comparator; import java.util.Iterator; import java ...

  2. 求小于等于k长度的最大区间和

    题意 给出一个序列,求长度小于等于k的最大区间和并输出起点和终点 1<=n<=100000 1<=k<=n   题解:先算出前缀和,利用单调队列的性质,在单调队列中存储sum[ ...

  3. zoj 2112 动态区间求第k大

    题目大意: 动态单点更新,然后多次询问求区间内第k大 这里单个的主席树不能实现,这里采取的是树状数组套主席树 首先可以想的是将静态主席树先构建好,不去动它,这里空间复杂度就是O(nlogn),这个只要 ...

  4. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

  5. HDU 4417 Super Mario(主席树 区间不超过k的个数)题解

    题意:问区间内不超过k的个数 思路:显然主席树,把所有的值离散化一下,然后主席树求一下小于等于k有几个就行.注意,他给你的k不一定包含在数组里,所以问题中的询问一起离散化. 代码: #include& ...

  6. E - Leading and Trailing 求n^k得前三位数字以及后三位数字,保证一定至少存在六位。

    /** 题目:E - Leading and Trailing 链接:https://vjudge.net/contest/154246#problem/E 题意:求n^k得前三位数字以及后三位数字, ...

  7. BZOJ2006:超级钢琴(ST表+堆求前K大区间和)

    Description 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符的美妙度 ...

  8. POJ1741--Tree (树的点分治) 求树上距离小于等于k的点对数

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 12276   Accepted: 3886 Description ...

  9. 求二叉树第K层的节点个数+求二叉树叶子节点的个数

    size_t _FindLeafSize(Node* root)     //求二叉树叶子节点的个数    {        //static size_t count = 0;        if ...

随机推荐

  1. 刘志梅2017710101152.《面向对象程序设计(java)》第十三周学习总结

    实验十三  图形界面事件处理技术 实验时间 2018-11-22 1.理论知识 (1)任何支持GUI的操作环境都要不断地监视按键或点击鼠标这样的事件. JAVA这样的面向对象语言,都将事件的相关的信息 ...

  2. 样式初始化(copy)

    css样式初始化reset文件 pc端 移动端 公共样式 1.pc端 /* normalize.css */ html { line-height: 1.15; /* 1 */ -ms-text-si ...

  3. 从javascript 调用angular的函数

    从vanilla javascript 调用angular的函数: * 调用 service中的函数var yourService = angular.element(document.body).i ...

  4. objective-c数组的七种遍历方法总结

    //第一种 [arr enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop){     NSLog(@"%ld,% ...

  5. IntelliJ IDEA 常用插件

    1.Alibaba Java Coding Guidelines(Java代码规约扫描插件) 阿里开发的此插件极大的改善程序员的代码质量,帮助程序员规范自己的代码 tools下可以切换中英文 地址:h ...

  6. OpenCL 矩阵乘法

    ▶ 矩阵乘法,按照书里的内容进行了几方面的优化,包括局部内存,矢量数据类型,寄存器,流水线等. ● 最直接的乘法.调用时 main.c 中使用 size_t globalSize[] = { rowA ...

  7. 关于各种O,DO/BO/DTO/VO/AO/PO

    阿里巴巴Java开发手册 链接:https://pan.baidu.com/s/11I9ViOrat-Bw_HA8yItXwA 密码:x5yi 2. DO/BO/DTO/VO/AO/PO PO(per ...

  8. 微信授权获取用户openId等信息

    在我们开发小程序的时候,需要通过授权获取用户的信息. 第一种使用wx.getUserInfo直接获取微信头像,昵称 // 必须是在用户已经授权的情况下调用 wx.getUserInfo({ succe ...

  9. python学习笔记---环境的安装,pip命令,数据类型,运算

    1.进入python环境: python 2:py -2 python 3:py -3 2.退出python环境 exit()/quit()/ctrl+z+enter ctrl+z+enter没有尝试 ...

  10. redis锁机制介绍与实例

    转自:https://m.jb51.net/article/154421.htm 今天小编就为大家分享一篇关于redis锁机制介绍与实例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要 ...