题目链接: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. maven项目自动创建src/main/resources等四个资源文件夹

    如何使maven项目自动创建这四个文件夹:src/main/resources.src/main/java.src/test/java.src/test/resources 网传甚广的在Config ...

  2. Java读取Excel并与SqlServer库中的数据比较

    先说说需求.在SQL server数据库中的表里存在一些数据,现在整理的Excel文档中也存在一些数据,现在需要通过根据比较某个字段值(唯一)来判断出,在库中有但excel中没有的数据. 大概的思路就 ...

  3. SQL FOR JSON PATH 返回 json

    --直接返回 age FOR JSON PATH --返回值 [{"name":"张学友","age":60}] select c1, c2 ...

  4. Windows下安装ZooKeeper

    Windows下安装ZooKeeper   一.简介 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组 ...

  5. json_encode($b, JSON_FORCE_OBJECT) 可以强制转换成对象

    最近在为移动端的项目提供接口,数据格式都为json,不过在过程中遇到一个小问题,代码如下: 情况一: $tmp = array('a','b','c'); echo json_encode($tmp) ...

  6. javascript 字符串与正则

    序:就是简单记录下正则的一些基础,还有cookie的一些设置获取删除 #字符串操作 search 查找 substring 获取子字符串 str.substring(start,stop) charA ...

  7. Oracle参数Arraysize设置对于逻辑读的影响分析

    说明: 当执行一条SQL查询的时候,为了获得满足的数据,查询在这个过程中完成解析,绑定,执行和提取数据等一系列步骤,这些步骤都是单独执行的,满足条件的数据行必须由数据库返回给应用:对于任何大小的结果集 ...

  8. vue+sass实现切换字体大小

    接到领导指示,用户嫌我做的页面字体太小,15px的字体叫小?领导说用户多是上了年纪的人.没办法,改吧,谁让咱是个搬砖的呢..咳咳 我寻思着这次改大了,下次用户嫌大再让改小呢?干脆给他做个选择字号的功能 ...

  9. mysql安装密码策略插件

    https://blog.csdn.net/kk185800961/article/details/79447754 注意linux安装密码策略的插件 validate_password.so

  10. css边框的一些属性

    边框样式值如下:none : 无边框.与任何指定的border-width值无关hidden : 隐藏边框.IE不支持dotted : 在MAC平台上IE4+与WINDOWS和UNIX平台上IE5.5 ...