K-th Number
Time Limit: 20000MS   Memory Limit: 65536K
Total Submissions: 50247   Accepted: 17101
Case Time Limit: 2000MS

Description

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment. 
That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?" 
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.

Input

The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000). 
The second line contains n different integer numbers not exceeding 109 by their absolute values --- the array for which the answers should be given. 
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).

Output

For each question output the answer to it --- the k-th number in sorted a[i...j] segment.

Sample Input

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

Sample Output

5
6
3

Hint

This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.

题目链接:POJ 2104    HDU 2665(HDU题面描述有误,虽说是求kth big其实还是跟POJ一样求kth small)

看主席树看了N久,终于把更新操作和查询操作看懂了:),其间找了无数篇博客但是其中绝大部分都是泛泛而谈有一些甚至只有代码连注释都没有,

很多次想弃疗(感觉这高大上的数据结构弱比我估计以后很难用到),然而还是没有放弃终于看懂一点点……故想通过这两道模版题来说一下弱比我的理解……

首先要知道普通线段树如何求整个范围内第k大,单点更新+单点查询(至叶子节点),然后就是这个区间第K大(小)怎么弄呢?由于单点更新的时候只会更新一条路上的东西即树上一层只会更新一个节点,比如某一层有两个节点来管理【1,2】与【 3,4】,假设更新1这个值,显然【3,4】是不会变的,只是管理【1,2】这个节点的cnt值加1,当然,这个节点下面还有【1,1】与【2,2】这俩叶子,同样道理也只会更新管理【1,1】的节点,使它的cnt值加1,管理【2,2】的节点不动……

根据二叉树的性质可以知道,每一次修改就只要新建出Log(N)左右个节点就可以与之前的树形成一个新的树,树上的老节点就是不用更新的,新节点就是更新的再指向老的节点(即很可能会与老节点共用子节点)。然后就用到了前缀和的思想,一个元素均不相同的序列可以发现这样一个规律,假设10个数为1 3 5 7 9 2 4 6 8 10,先简化一下问题,求【3,7】的节点cnt为多少,那显然是【1,7】.cnt-【1,(3-1)】.cnt=5,继而就可以求【3,4】、【5,7】甚至更后面的小区间内cnt值,那问题就成了一个前面讲的线段树求整个范围内第k大,只是整个范围一直被维护为L,R之间而已。

主席树这种写法需要root数组、和lson与rson的数组,还有记录区间内数字出现个数的cnt数组

更新操作:首先复制当前所在原节点的所有信息,再将cnt+1(因为你在原来的基础上插入了一个数),然后用单点更新的思路进行复制并更新(讲复制感觉更贴切实际,因为更新是在副本上更新的而不是原来节点上),走到哪就把哪个节点复制下来再在这个副本上进行更新

查询操作:用两颗不同的树的cnt作差再判断查询方向,写法上跟普通的单点查询非常相似

最后题目数据范围比较大需要离散化一下另外我加了个没什么卵用的读入外挂……

POJ 2104代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <vector>
using namespace std;
#define INF 0x3f3f3f3f
#define CLR(x,y) memset(x,y,sizeof(x))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const int N=100010;
struct seg
{
int lson,rson;
int cnt;
};
seg T[N*20];
int tot,rt[N];
int arr[N];
vector<int>pos;
void init()
{
tot=0;
CLR(rt,0);
pos.clear();
}
int Scan()
{
int res=0,ch,flag=0;
if((ch=getchar())=='-')
flag=1;
else if(ch>='0'&&ch<='9')
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9')
res=res*10+ch-'0';
return flag?-res:res;
}
void build(int &cur,int l,int r)//注意第一个参数是引用为了修改原值
{
cur=++tot;
T[cur].cnt=0;
if(l==r)
return ;
else
{
int mid=MID(l,r);
build(T[cur].lson,l,mid);
build(T[cur].rson,mid+1,r);
}
}
void update(int &cur,int ori,int l,int r,int val)//cur为准备新开的节点,ori为原来对应位置的节点
{
cur=++tot;//新建一颗树
T[cur].lson=T[ori].lson;//复制信息
T[cur].rson=T[ori].rson;//复制信息
T[cur].cnt=T[ori].cnt+1;//更新新增值
if(l==r)
return ;
else
{
int mid=MID(l,r);
if(val<=mid)
update(T[cur].lson,T[ori].lson,l,mid,val);
else
update(T[cur].rson,T[ori].rson,mid+1,r,val);
}
}
int query(int st,int ed,int l,int r,int k)
{
if(l==r)
return l;
else
{
int mid=MID(l,r);
int cnt=T[T[ed].lson ].cnt-T[T[st].lson ].cnt;//与普通求Kth一样先看lson(看rson也行下面顺序变一下而已)
if(k<=cnt)
return query(T[st].lson,T[ed].lson,l,mid,k);
else
return query(T[st].rson,T[ed].rson,mid+1,r,k-cnt);
}
}
int main(void)
{
int n,m,i,l,r,k;
while (~scanf("%d%d",&n,&m))
{
for (i=1; i<=n; ++i)
{
scanf("%d",&arr[i]);
pos.push_back(arr[i]);
} sort(pos.begin(),pos.end());//pos储存着原值
pos.erase(unique(pos.begin(),pos.end()),pos.end());
for (i=1; i<=n; ++i)
arr[i]=lower_bound(pos.begin(),pos.end(),arr[i])-pos.begin()+1;//arr变为离散化之后的数组仅用来更新 int SZ=pos.size();
build(rt[0],1,SZ); for (i=1; i<=n; ++i)
update(rt[i],rt[i-1],1,SZ,arr[i]);
for (i=0; i<m; ++i)
{
l=Scan();
r=Scan();
k=Scan();
int indx=query(rt[l-1],rt[r],1,SZ,k)-1;//由于pos下标从0开始
printf("%d\n",pos[indx]);//再映射回去
}
init();
}
return 0;
}

搞半天发现build没什么卵用……直接update也行,离散化也优化了一下,简化了HDU的代码,速度快了不少

HDU代码:

#include <stdio.h>
#include <algorithm>
#include <vector>
#include <string.h>
using namespace std;
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define MID(x,y) ((x+y)>>1) const int N=1e5+7;
struct seg
{
int lson,rson;
int cnt;
};
seg T[N*20];
int tot,root[N];
int arr[N];
vector<int>vec; void init()
{
tot=0;
vec.clear();
CLR(root,0);
}
void update(int &cur,int ori,int l,int r,int val)
{
cur=++tot;
T[cur]=T[ori];
++T[cur].cnt;
if(l==r)
return ;
int mid=MID(l,r);
if(val<=mid)
update(T[cur].lson,T[ori].lson,l,mid,val);
else
update(T[cur].rson,T[ori].rson,mid+1,r,val);
}
int query(int L,int R,int l,int r,int k)
{
if(l==r)
return l;
int mid=MID(l,r);
int cnt=T[T[R].lson].cnt-T[T[L].lson].cnt;
if(k<=cnt)
return query(T[L].lson,T[R].lson,l,mid,k);
else
return query(T[L].rson,T[R].rson,mid+1,r,k-cnt);
}
int main(void)
{
int T,n,m,l,r,k,i;
scanf("%d",&T);
while (T--)
{
init();
scanf("%d%d",&n,&m);
for (i=1; i<=n; ++i)
{
scanf("%d",&arr[i]);
vec.push_back(arr[i]);
} sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end()); int R=vec.size()-1;//直接离散为0~(size-1)方便后面就不用搞什么+1-1了
//build(root[0],0,R);
for (i=1; i<=n; ++i)
{
arr[i]=lower_bound(vec.begin(),vec.end(),arr[i])-vec.begin();
update(root[i],root[i-1],0,R,arr[i]);
}
for (i=0; i<m; ++i)
{
scanf("%d%d%d",&l,&r,&k);
int indx=query(root[l-1],root[r],0,R,k);
printf("%d\n",vec[indx]);
}
}
return 0;
}

POJ 2104&HDU 2665 Kth number(主席树入门+离散化)的更多相关文章

  1. hdu 2665 Kth number 主席树

    Kth number Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Prob ...

  2. HDU - 2665 Kth number 主席树/可持久化权值线段树

    题意 给一个数列,一些询问,问$[l,r]$中第$K$大的元素是哪一个 题解: 写法很多,主席树是最常用的一种之一 除此之外有:划分树,莫队分块,平衡树等 主席树的定义其实挺模糊, 一般认为就是可持久 ...

  3. poj2104 k-th number 主席树入门讲解

    poj2104 k-th number 主席树入门讲解 定义:主席树是一种可持久化的线段树 又叫函数式线段树   刚开始学是不是觉得很蒙逼啊 其实我也是 主席树说简单了 就是 保留你每一步操作完成之后 ...

  4. hdu 2665 Kth number(划分树模板)

    http://acm.hdu.edu.cn/showproblem.php?pid=2665 [ poj 2104 2761 ]  改变一下输入就可以过 http://poj.org/problem? ...

  5. HDU 2665 Kth number(划分树)

    Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total S ...

  6. hdu 2665 Kth number

    划分树 /* HDU 2665 Kth number 划分树 */ #include<stdio.h> #include<iostream> #include<strin ...

  7. 主席树[可持久化线段树](hdu 2665 Kth number、SP 10628 Count on a tree、ZOJ 2112 Dynamic Rankings、codeforces 813E Army Creation、codeforces960F:Pathwalks )

    在今天三黑(恶意评分刷上去的那种)两紫的智推中,突然出现了P3834 [模板]可持久化线段树 1(主席树)就突然有了不详的预感2333 果然...然后我gg了!被大佬虐了! hdu 2665 Kth ...

  8. poj 2104 K-th Number 主席树+超级详细解释

    poj 2104 K-th Number 主席树+超级详细解释 传送门:K-th Number 题目大意:给出一段数列,让你求[L,R]区间内第几大的数字! 在这里先介绍一下主席树! 如果想了解什么是 ...

  9. SPOJ MKTHNUM & POJ 2104 - K-th Number - [主席树模板题]

    题目链接:http://poj.org/problem?id=2104 Description You are working for Macrohard company in data struct ...

随机推荐

  1. 【python】choice函数

    来源:http://www.runoob.com/python/func-number-choice.html 描述 choice() 方法返回一个列表,元组或字符串的随机项. 语法 以下是 choi ...

  2. [MAC] Mac下的SVN命令行

    转载自: http://www.cnblogs.com/snandy/p/4072857.html Mac自带了SVN命令行,如我的升级到10.10(OSX yosemite)后命令行版本为1.7.1 ...

  3. [Android UI] shape和selector的结合使用

    shape和selector是Android UI设计中经常用到的,比如我们要自定义一个圆角Button,点击Button有些效果的变化,就要用到shape和selector.可以这样说,shape和 ...

  4. Road Construction(poj 3352)

    题意:求最少天几条边,使这个无向图变成双连通图. /* tarjan缩点后,形成一棵树,求出叶子节点数tot,答案是(tot+1)/2 */ #include<cstdio> #inclu ...

  5. 【Ubuntu14.04.1】设置开机可以Root用户身份登录

    $ sudo gedit /usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf [SeatDefaults]user-session=ubuntugreet ...

  6. Hadoop 1.1.2 eclipse plugin 编译 win7 集成

    Windows平台上使用ANT编译Hadoop Eclipse Plugin 一.准备工作:   1.安装JDK 下载页面:http://www.oracle.com/technetwork/java ...

  7. 字符串匹配与KMP算法实现

    >>字符串匹配问题 字符串匹配问题即在匹配串中寻找模式串是否出现, 首先想到的是使用暴力破解,也就是Brute Force(BF或蛮力搜索) 算法,将匹配串和模式串左对齐,然后从左向右一个 ...

  8. Java Hour 45 Hibernate ( 2 )

    基本确定了,一个月后也就是在2014年的开端,我将离开这个公司. 所以我大概还有30个学时. 45.1 你需要一个数据库 首先,必须有一个试验用的数据库,这里我们使用MySQL. 尽管书中的说明是使用 ...

  9. win8 鼠标失灵解决办法

    前几天 也没更新,却不知道突然win8 pro 失灵了,是不是ms 后台运行的也不确定,不过更新之后就可以用了. 经供参考: 更新前: 更新后: 我的主板是华硕的,有时候需要重启几次鼠标才显示出来

  10. sql server存儲過程語法

    -- 变量的声明,sql里面声明变量时必须在变量前加@符号    DECLARE @I INT -- 变量的赋值,变量赋值时变量前必须加set    SET @I = 30 -- 声明多个变量    ...