区间求小于等于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 ...
随机推荐
- CF963D Frequency of String
https://codeforces.com/problemset/problem/123/D 题目大意 给一个字符串 \(s\),每次询问一个字符串 \(m_i\) 和一个正整数 \(k_i\),问 ...
- DB2锁与隔离级别
数据库管理器支持三种一般类别的锁定: 共享(S) 挂起 S 锁定之后,并发应用程序进程只能对数据执行只读操作. 更新(U) 挂起 U 锁定之后,如果并发应用程序进程未声明它们要更新行,那么它们只能对数 ...
- Wow64(32位进程)注入DLL到64位进程
转载自: https://blog.poxiao.me/p/wow64-process-inject-dll-into-x64-process/ 向其他进程注入DLL通常的做法是通过调用CreateR ...
- 决策树(decision tree)
决策树是一种常见的机器学习模型.形象地说,决策树对应着我们直观上做决策的过程:经由一系列判断,得到最终决策.由此,我们引出决策树模型. 一.决策树的基本流程 决策树的跟节点包含全部样例,叶节点则对应决 ...
- PHP 实现多网站共享用户SESSION 数据解决方案
PHP 实现多网站共享用户SESSION 数据解决方案 来源URL:http://blog.csdn.net/dongdongzzcs/article/details/6906613 一.问题起源 稍 ...
- C#将时间转化自定义类型格式,C#获取时间间隔
C# string.Format格式化日期: DateTime dt = ,,,,,,); string.Format("{0:y yy yyy yyyy}",dt); //17 ...
- QAC静态测试配置及使用教程
使用前提:安装成功QAC软件. . 1.打开软件如上 . 2.file->Auto-Create-Project,出现如下所示对话框 1-工程名字 2-将要分析的代码路径 3-代码报告输出路径 ...
- 微信授权获取用户openId等信息
在我们开发小程序的时候,需要通过授权获取用户的信息. 第一种使用wx.getUserInfo直接获取微信头像,昵称 // 必须是在用户已经授权的情况下调用 wx.getUserInfo({ succe ...
- ToroiseSVN和VisualSVN-server的配置使用, 外网访问SVN 版本库
https://www.cnblogs.com/Leo_wl/p/3475167.html
- python之路:列表及元组之定义
python开发之路:列表及元组之定义 列表是以后用处较大的一个数据类型,这种数据类型可以存储按组分类的信息.好了,我不多说,开始讲了! 好了,现在我有个情景,我要存东汉时期(韩国,秦国,……)所 ...