题意:

  有n个位置,每个位置可以看做一个集合,现在要求你实现一个数据结构支持以下功能:

  1:在a-b的集合中插入一个数

  2:询问a-b集合中所有元素的第k大.

SOL:

  调得火大! 李建说数据结构题能锻炼人,然而我的水平还是太低啊!每次调这种题到最后往往都会变成找不同...日狗!

  树套树的第一题,但是是权值线段树套区间线段树,与心中真正的树套树还是有一点差距-----线段树套平衡树(一直打不完..卡死在不知什么地方).

  对于权值线段树套区间线段树的想法,我们可以这么来看,先考虑在一个序列上询问第k大(不是这个序列的区间,也没有修改),那么我们可以通过线段树维护的两个区间的数的个数----权值线段树,维护插入的数,像我们搞逆序对的那个线段树-------来查询.当我们带上插入操作,那么我们可以在这权值线段树上维护一棵区间线段树,维护的是在外层维护的权值范围内,相应位置的个数.  仔细想仔细想仔细体会.非常有意思.

  带上查询大概就明白了,当我们要查询一个区间(a,b)的第k大,我们想知道的信息有什么呢?有哪些数,这些数在这个区间出现了多少次. 那么这棵线段树可以很好地实现,查询右儿子即在mid---n这些数在a,b上出现了多少次,如果小于k,那么显然这个数只可能在1-mid间,我们只要查询左子树即可.

  这样建树似乎很耗空间,至于省空间的方法,以及无比奇妙的分治方法,以及秒杀一切的套平衡树方法,都一一去实现吧.

CODE:

  代码还是很短的,但是两棵线段树之间的逻辑关系,以及各种点,让调试变得无比困难,所以要一次打对,这也是代码能力的体现吧

/*==========================================================================
# Last modified: 2016-02-23 19:07
# Filename: 3110.cpp
# Description:
==========================================================================*/
#define me AcrossTheSky
#include <cstdio>
#include <cmath>
#include <ctime>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm> #include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector> #define lowbit(x) (x)&(-x)
#define FOR(i,a,b) for((i)=(a);(i)<=(b);(i)++)
#define FORP(i,a,b) for(int i=(a);i<=(b);i++)
#define FORM(i,a,b) for(int i=(a);i>=(b);i--)
#define ls(a,b) (((a)+(b)) << 1)
#define rs(a,b) (((a)+(b)) >> 1)
#define getlc(a) ch[(a)][0]
#define getrc(a) ch[(a)][1] #define maxn 15000000
#define maxm 100000
#define pi 3.1415926535898
#define _e 2.718281828459
#define INF 1070000000
using namespace std;
typedef long long ll;
typedef unsigned long long ull; template<class T> inline
void read(T& num) {
bool start=false,neg=false;
char c;
num=0;
while((c=getchar())!=EOF) {
if(c=='-') start=neg=true;
else if(c>='0' && c<='9') {
start=true;
num=num*10+c-'0';
} else if(start) break;
}
if(neg) num=-num;
}
/*==================split line==================*/
int sum[maxn],ch[maxn][2],v[maxn],lazy[maxn],root[maxn];
int L,R,m,n,cnt=0;
//void pushup(int node,int l,int r){sum[node]=sum[ch[node][1]]+sum[ch[node][0]]+lazy[node]*(r-l+1);}
int _count(int node,int l,int r){
if (L<=l && r<=R) return sum[node];
int mid=rs(l,r),t=0;
if (L<=mid) t+=_count(ch[node][0],l,mid);
if (R>mid) t+=_count(ch[node][1],mid+1,r);
return t+lazy[node]*(min(r,R)-max(L,l)+1);
}
int query(int node,int l,int r,int k){
if (l==r) return l;
int mid=rs(l,r),lc=ls(node,0),rc=lc|1;
int t=_count(root[lc],1,n);
if (t>=k) query(lc,l,mid,k);
else query(rc,mid+1,r,k-t);
}
void updata(int &o,int l,int r){
if (!o) o=++cnt;
if (L<=l && r<=R) {sum[o]+=r-l+1; lazy[o]++; return ;}
int mid=rs(l,r);
if (L<=mid) updata(ch[o][0],l,mid);
if (R>mid) updata(ch[o][1],mid+1,r);
//pushup(o,l,r);
sum[o]=sum[ch[o][1]]+sum[ch[o][0]]+lazy[o]*(r-l+1);
}
void insert(int node,int l,int r,int x){
//if (root[node]==0) root[node]=++cnt;
updata(root[node],1,n);
if (l==r) return;
int mid=rs(l,r),lc=ls(node,0),rc=lc|1;
if (x<=mid) insert(lc,l,mid,x);
else insert(rc,mid+1,r,x);
}
int main(){
read(n); read(m);
memset(root,0,sizeof(root));
FORP(i,1,m){
int flag,x,y;
int k;
read(flag); read(L); read(R); read(k);
if (flag==1) k=n-k+1,insert(1,1,n,k);
else printf("%d\n",n-query(1,1,n,k)+1);
}
}

BZOJ 3110 k大数查询 & 树套树的更多相关文章

  1. BZOJ 3110 K大数查询 | 整体二分

    BZOJ 3110 K大数查询 题面 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个 ...

  2. [BZOJ]3110 K大数查询(ZJOI2013)

    这大概是唯一一道小C重写了4次的题目. 姿势不对的树套树(Fail) → 分块(Fail) → 整体二分(Succeed) → 树套树(Succeed). 让小C写点心得静静. Description ...

  3. bzoj 3110 K大数查询

    第一道整体二分,因为只需要知道每个询问区间中比mid大的数有多少个,就可以直接用线段树区间加,区间求和了. #include<iostream> #include<cstdio> ...

  4. P3332 [ZJOI2013]K大数查询(线段树套线段树+标记永久化)

    P3332 [ZJOI2013]K大数查询 权值线段树套区间线段树 把插入的值离散化一下开个线段树 蓝后每个节点开个线段树,维护一下每个数出现的区间和次数 为了防止MLE动态开点就好辣 重点是标记永久 ...

  5. BZOJ3110 K大数查询 【线段树 + 整体二分 或 树套树(非正解)】

    Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位 ...

  6. 【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改

    题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数 ...

  7. BZOJ.3110.[ZJOI2013]K大数查询(整体二分 树状数组/线段树)

    题目链接 BZOJ 洛谷 整体二分求的是第K小(利用树状数组).求第K大可以转为求第\(n-K+1\)小,但是这样好像得求一个\(n\). 注意到所有数的绝对值\(\leq N\),将所有数的大小关系 ...

  8. [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)

    [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ...

  9. BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

    3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[Submit][Sta ...

随机推荐

  1. 三、jQuery--jQuery基础--jQuery基础课程--第12章 jQuery在线聊天室

    在线聊天室案例 一.功能简介: 1.用户需要登录后才能进入聊天室交流 2.已无刷新的方式,动态展示交流后的内容和在线人员的基本信息 3.登录后的用户可以提交文字和表情图标 技术重点:利用ajax的无刷 ...

  2. Android View -- setTranslationX

    通过此方法使View位置发生偏移,达到margin的作用却又不改变View的getLeft()的值. 恢复方法是setTranslationX(0),而不是上一次偏移量的相反数. 不过,通过getLo ...

  3. rpm -qc 来查找安装包的配置文件

    rpm -qc elasticsearch /etc/elasticsearch/elasticsearch.yml /etc/elasticsearch/jvm.options /etc/elast ...

  4. 【javascript】 for循环小技巧

    最近在读[Jquery技术内幕],里面介绍了一种js for循环的实用写法. 一般写for循环是这么写的: var elemts = [1,2,3,4,5]; for(var i=0; i<el ...

  5. CentOS FTP基于虚拟用户的配置

    详细可以看:http://www.linuxidc.com/Linux/2013-12/94242.htm 所谓虚拟用户就是没有使用真实的帐户,只是通过映射到真实帐户和设置权限的目的.虚拟用户不能登录 ...

  6. 攻城狮在路上(叁)Linux(十八)--- 文件系统的简单操作

    本篇仅作为补漏. 一.查看磁盘和目录的容量:df  du df:列出文件系统的整体磁盘使用量. du:评估文件系统的磁盘使用量(常用于评估目录所占容量) 二.连接文件:ln 1.hard link:硬 ...

  7. 攻城狮在路上(壹) Hibernate(六)--- 通过Hibernate操纵对象(上)

    一.Hibernate缓存简介: Session接口是Hibernate向应用程序提供的操纵数据接口的最主要接口,它提供了基本的保存.更新.删除和加载Java对象的方法. Session具有一个缓存, ...

  8. <转>ORA-12154: TNS: 无法解析指定的连接标识符

    相信作为ORACLE数据库的开发人员没有少碰到“ORA-12154: TNS: 无法解析指定的连接标识符”,今天我也又碰到了类似的情况,将我的解决方法进行小结,希望能对碰到同样问题的友人们提供帮助. ...

  9. js setTimeout运用

    js setTimeout运用 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "ht ...

  10. PHPCMS 多站点管理切换问题

    打开系统函数库global.func.php 可以看到获取站点ID的函数如下 /** * 获取当前的站点ID */ function get_siteid() { static $siteid; if ...