区间求小于等于k的数字个数 hdu4177
题目链接: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的更多相关文章
- 面试题:m个长度为n的ordered array,求top k 个 数字
package com.sinaWeibo.interview; import java.util.Comparator; import java.util.Iterator; import java ...
- 求小于等于k长度的最大区间和
题意 给出一个序列,求长度小于等于k的最大区间和并输出起点和终点 1<=n<=100000 1<=k<=n 题解:先算出前缀和,利用单调队列的性质,在单调队列中存储sum[ ...
- zoj 2112 动态区间求第k大
题目大意: 动态单点更新,然后多次询问求区间内第k大 这里单个的主席树不能实现,这里采取的是树状数组套主席树 首先可以想的是将静态主席树先构建好,不去动它,这里空间复杂度就是O(nlogn),这个只要 ...
- 计蒜客 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 ...
- HDU 4417 Super Mario(主席树 区间不超过k的个数)题解
题意:问区间内不超过k的个数 思路:显然主席树,把所有的值离散化一下,然后主席树求一下小于等于k有几个就行.注意,他给你的k不一定包含在数组里,所以问题中的询问一起离散化. 代码: #include& ...
- E - Leading and Trailing 求n^k得前三位数字以及后三位数字,保证一定至少存在六位。
/** 题目:E - Leading and Trailing 链接:https://vjudge.net/contest/154246#problem/E 题意:求n^k得前三位数字以及后三位数字, ...
- BZOJ2006:超级钢琴(ST表+堆求前K大区间和)
Description 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符的美妙度 ...
- POJ1741--Tree (树的点分治) 求树上距离小于等于k的点对数
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 12276 Accepted: 3886 Description ...
- 求二叉树第K层的节点个数+求二叉树叶子节点的个数
size_t _FindLeafSize(Node* root) //求二叉树叶子节点的个数 { //static size_t count = 0; if ...
随机推荐
- gin的url查询参数解析
gin作为go语言最知名的网络库,在这里我简要介绍一下url的查询参数解析.主要是这里面存在一些需要注意的地方.这里,直接给出代码,和运行结果,在必要的地方进行分析. 代码1: type Struct ...
- centos7安装nginx,以及使用node测试反向代理
1.添加nginx的安装源 vi /etc/yum.repos.d/nginx.repo 2.输入下面内容,并保存退出 [nginx] name=nginx repo baseurl=http://n ...
- 转载:揪出MySQL磁盘消耗迅猛的真凶
揪出MySQL磁盘消耗迅猛的真凶 背景 Part1:写在最前 当一张单表10亿数据量的表放在你面前,你将面临着什么? Part2:背景介绍 为了提升数据库资源利用率,一个实例中,在不互相影响,保证业务 ...
- Centos7下GlusterFS分布式存储集群环境部署记录
0)环境准备 GlusterFS至少需要两台服务器搭建,服务器配置最好相同,每个服务器两块磁盘,一块是用于安装系统,一块是用于GlusterFS. 192.168.10.239 GlusterFS-m ...
- centos7 安装远程桌面
https://www.linuxidc.com/Linux/2017-09/147050.htm https://blog.csdn.net/dazhi_1314/article/details/7 ...
- CSS3之3D立方体效果
下面代码可实现3D立方体,比较好理解,就是让每个面先平移到指定位置,然后旋转90度 <!DOCTYPE html> <html lang="en"> < ...
- 常见cmd命令,开发人员必备
运行程序 notepad--------打开记事本 calc-----------启动计算器 regedit.exe-------注册表 write----------写字板 mmc--------- ...
- Centos6、7下安装Nginx的两种方法
一,通过yum命令安装 1.操作系统版本的确认很重要,因为下一步我们要依靠这个来创建yum源文件 2.创建 nginx.repo vi /etc/yum.repos.d/nginx.repo 文件内容 ...
- 初识异步、并发处理纯代码及Demo
多线程Thread 处理 Thread thread = new Thread(()=> { ; i < ; i++) { Console.WriteLine("这是第" ...
- 用ASP.NET_Regsql.exe创建Session数据库
CMD: C:\Users\ZhangSC>C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_regsql.exe -S ZhangS ...