题目链接: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. 非阻塞I/O事件驱动

    在 Java.PHP 或者.net 等服务器端语言中,会为每一个客户端连接创建一个新的线程.而每个线程需要耗费大约 2MB 内存.也就是说,理论上,一个 8GB 内存的服务器可以同时连接的最大用户数为 ...

  2. uWSGI的配置与发布

    参考:   https://www.cnblogs.com/pyyu/p/9481344.html 一.什么是wsgi, uwsgi, uWSGI wsgi 全称web server gateway ...

  3. Go 1.11 Module 介绍

    title: "Go 1.11 Module" date: 2018-10-26T23:50:56+08:00 draft: false --- Go 1.11 Module 介绍 ...

  4. (转)SQLServer_十步优化SQL Server中的数据访问 二

    原文地址:http://tech.it168.com/a2009/1125/814/000000814758_all.shtml 第五步:识别低效TSQL,采用最佳实践重构和应用TSQL 由于每个程序 ...

  5. html5编写软件哪个好?八款html5编写软件推荐

    随着各大浏览器对HTML5技术支持的不断完善,未来HTML5必将改变我们创建Web应用程序的方式.而很多html5的初学者都想找一款好用的编写软件,这里主机吧就给大家推荐七款好用的html5编写软件. ...

  6. express+websocket+exec+spawn=webshell

    var child_process = require('child_process'); var ws = require("nodejs-websocket"); consol ...

  7. docker 搭建 web 服务环境

    docker容器虽然早就听说过,但是本人还真的没去用过,刚好看到相关的文章,就分享了下,有机会可以实践下...... 做过开发的人对开发环境的安装.配置应该都不会太陌生,不管你做什么开发,对开发环境都 ...

  8. spring boot 整合 云之讯 demo

    ---恢复内容开始--- package com.zhourong.controller; import org.apache.commons.lang3.RandomStringUtils; imp ...

  9. App专项测试之弱网测试

    转载 https://blog.csdn.net/TestingGDR/article/details/83059415

  10. 监测c3动画过渡完成的事件

    监测css3动画完成的事件 transitionend 用法: let element = document.getElementById("slidingMenu"); elem ...