题意:给定一个序列A,接下来又m个询问,每个询问输出A[L,R]中的第K大。(保证第k大存在)

思路:

  我想拿来练习“可持久化线段树”的,搜到这个比较巧的算法也可以解决这个问题,叫“归并树?。大概的思想就是和线段树一样,只是线段树上的每个非叶子节点是一个区间,等于该节点的两个孩子节点的区间的拼接起来,而每个区间内保持有序的。那么在查找时就找到这两个区间,二分枚举答案然后在询问区间[L,R]判断否排第k。这里二分答案只需要在线段树的根进行就行了,因为根这个区间是有序的。查找时[L,R]可能会是两个区间的拼接的[L,mid]+[mid+1,R],所以要在两个区间中分别判断val排行老几,然后加起来就是其在[L,R]的真实排行了,这可以用low_bound函数实现。

 //#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
#define pii pair<int,int>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int N=;
int seq[N], tree[][N]; void build_tree(int L,int R,int depth)
{
if(L==R)
{
tree[depth][L]=seq[L];
return;
}
int mid=(L+R)>>;
build_tree(L,mid,depth+);
build_tree(mid+,R,depth+); //归并
int first=L, second=mid+, cnt=L;
while( first<=mid && second<=R )
{
if(tree[depth+][first]<tree[depth+][second])
tree[depth][cnt++]=tree[depth+][first++];
else
tree[depth][cnt++]=tree[depth+][second++];
}
if(first<=mid) //左边未完
{
for(int i=first; i<=mid; i++)
tree[depth][cnt++]=tree[depth+][i];
}
else //右边未完
{
for(int i=second; i<=R; i++)
tree[depth][cnt++]=tree[depth+][i];
}
} int query(int ll,int rr,int L,int R,int val,int depth) //返回val在[L,R]内的排名-1
{
if(L==ll && rr==R)
return lower_bound( &tree[depth][L], &tree[depth][R+], val)
-&tree[depth][L];
int mid=(ll+rr)>>, cnt=;
if( R<=mid ) cnt+=query(ll,mid, L,R, val,depth+);
else if( L>mid ) cnt+=query(mid+,rr, L,R, val,depth+);
else
{
cnt+=query(ll,mid, L,mid, val,depth+);
cnt+=query(mid+,rr, mid+,R, val,depth+);
}
return cnt;
} int main()
{
freopen("input.txt", "r", stdin);
int n, m, L, R, k;
while(~scanf("%d%d",&n,&m))
{
for(int i=; i<=n; i++) scanf("%d",&seq[i]);
build_tree(, n, ); while(m--)
{
scanf("%d%d%d",&L,&R,&k);
k--;
int ll=, rr=n;
while( ll<rr ) //在tree[0]中二分这个数
{
int mid=ll+(rr-ll+)/;
int pos=query(,n, L,R, tree[][mid],);
if( pos<=k ) ll=mid; //所查找的数太小了
else rr=mid-;
}
printf("%d\n", tree[][ll]);
}
}
return ;
}

AC代码

  主席树解法:按照序列的顺序seq[i],每插入1个点就建1棵树,而每棵树中有且只有seq[1,i]这个序列,而且不是按照seq[1,i]的顺序,而是变成在该树中是有序的。

  举例:假设有序列seq[4]={1,3,2,4}。

  插入第seq[1]后的结果:

  

  插入第seq[2]后的结果:

  

  插入第seq[3]后的结果:

  

  插入第seq[4]后的结果:

  

  观察上面的4张图,红色的点表示是不同于上一幅图的的点,即是新创建的的点。可以看到,每次插入后最多仅有logn个点会被创建。插入是按照有序的方式插入的,比如新插入seq[2]=2,那么其应该排在第二,所以我们需要事先对seq进行排序,才能知道seq[i]的具体应该插在什么位置。

  得到这些图就可以O(logN)知道第k个数了,比如要在区间[3,4]中找k=1的数字,那么只需要根据root[2]和root[4]就可以算出,只需要在每个节点上用个计数器cnt表示该子树的节点数,具体的话不难算的,自己研究下图吧。

 //#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#include <vector>
#include <iostream>
#define pii pair<int,int>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int N=;
struct Node
{
int L, R, cnt;
}nod[N*]; //线段树上的节点
struct Seq
{
int val,idx;
bool operator < ( const Seq &t ) const{return val<t.val;}
}seq[N]; //序列
int rank[N], root[N], node_cnt; void insert(int rk,int &t,int L,int R) //每次插入,就建1棵新树
{
nod[node_cnt]=nod[t];
t=node_cnt++;
nod[t].cnt++; //此子树的叶子节点数 if(L==R) return ; //到底了。只存此子树的节点数 int mid=(L+R)>>;
if(rk<=mid) insert(rk,nod[t].L, L,mid);
else insert(rk,nod[t].R, mid+,R);
} int query(int t1,int t2,int k,int L,int R)
{
if(L==R) return R; //返回的是“有序序列”的下标
int L1=nod[t1].L, L2=nod[t2].L; //两棵树的左子树节点数量
int left=nod[L2].cnt-nod[L1].cnt; //用于判断第k大在左/右
int mid=(L+R)>>; if(k<=left) query(nod[t1].L, nod[t2].L, k, L, mid); //在左边
else query(nod[t1].R, nod[t2].R, k-left, mid+, R );
} int main()
{
//freopen("input.txt", "r", stdin);
int n, m, L, R, k;
while(~scanf("%d%d",&n,&m))
{
node_cnt=;
memset(root, , sizeof(root));
for(int i=; i<=n; i++)
{
scanf("%d",&seq[i].val);
seq[i].idx=i;
}
sort(seq+,seq+n+); //需先排序
for(int i=; i<=n; i++) //反向索引
rank[ seq[i].idx ]=i;
for(int i=; i<=n; i++) //按原序逐个插入
{
root[i]=root[i-];
insert(rank[i], root[i], , n);
}
while(m--)
{
scanf("%d%d%d",&L,&R,&k);
int idx=query(root[L-], root[R], k, , n); //两树可以同时进行
printf("%d\n", seq[idx].val);
}
}
return ;
}

AC代码

POJ 2104 K-th Number (区间第k大)的更多相关文章

  1. poj 2104 主席树(区间第k大)

    K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 44940   Accepted: 14946 Ca ...

  2. [poj 2104]主席树+静态区间第k大

    题目链接:http://poj.org/problem?id=2104 主席树入门题目,主席树其实就是可持久化权值线段树,rt[i]维护了前i个数中第i大(小)的数出现次数的信息,通过查询两棵树的差即 ...

  3. POJ 2014.K-th Number 区间第k小 (归并树)

    K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 57543   Accepted: 19893 Ca ...

  4. HDU 2665.Kth number 区间第K小

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

  5. POJ2104 K-th Number —— 区间第k小 整体二分

    题目链接:https://vjudge.net/problem/POJ-2104 K-th Number Time Limit: 20000MS   Memory Limit: 65536K Tota ...

  6. POJ 2104:K-th Number(整体二分)

    http://poj.org/problem?id=2104 题意:给出n个数和m个询问求区间第K小. 思路:以前用主席树做过,这次学整体二分来做.整体二分在yr大佬的指点下,终于大概懂了点了.对于二 ...

  7. BZOJ 3110([Zjoi2013]K大数查询-区间第k大[段修改,在线]-树状数组套函数式线段树)

    3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec   Memory Limit: 512 MB Submit: 418   Solved: 235 [ Submit][ ...

  8. POJ 2104 K-th Number(区间第k大数)(平方切割,归并树,划分树)

    题目链接: http://poj.org/problem? id=2104 解题思路: 由于查询的个数m非常大.朴素的求法无法在规定时间内求解. 因此应该选用合理的方式维护数据来做到高效地查询. 假设 ...

  9. POJ 2104:K-th Number(主席树静态区间k大)

    题目大意:对于一个序列,每次询问区间[l,r]的第k大树. 分析: 主席树模板题 program kthtree; type point=record l,r,s:longint; end; var ...

  10. POJ-2104-K-th Number(区间第K大+主席树模板题)

    Description You are working for Macrohard company in data structures department. After failing your ...

随机推荐

  1. web面试常见问题

    1事件继承 function ClassA(sColor) {     this.color = sColor;     this.sayColor = function () {        al ...

  2. Visual Studio 2017 本地调试 Chrome浏览器自动退出

    在使用VS 2017(15..6 .15.7)对.NET Core MVC应用程序进行本地调试的时候,选择使用Chrome浏览器.但输入中文 就自动关闭Chrome浏览器,随后结束调试.但复制.粘贴中 ...

  3. windows下mysql5.1忘记root密码解决方法[win7]

    步骤如下:1.停止mysql服务(以管理员身份,在cmd命令行下运行) net stop mysql2.使用 mysqld –skip-grant-tables 命令启动mysql数据库 D:\> ...

  4. 爬虫中Xpath有时候是无效的

    http://www.cnblogs.com/lchzls/p/6282790.html给出了两种方案

  5. C#基础:使用Thread创建线程

    Thread类可以创建和控制线程,Thread类的构造函数重载为接受ThreadStart和ParameterizedThreadStart类型的委托参数.下面我们用一个例子来解释怎样用Thread类 ...

  6. Sping中使用Junit进行测试

    分析: 1.应用程序的入口 main方法2.junit单元测试中,没有main方法也能执行 junit集成了一个main方法 该方法就会判断当前测试类中哪些方法有 @Test注解 junit就让有Te ...

  7. KING_Unity学习之UGUI_Canvas渲染顺序以及层次关系总结

    http://blog.csdn.net/kingsea168/article/details/50252733 之前一直用NGUI开发界面,但看到现在的unity的新版本的UGUI也不错,这几天专门 ...

  8. Educational Codeforces Round 18D(完全二叉树中序遍历&lowbit)

    题目链接:http://codeforces.com/contest/792/problem/D 题意:第一行输入n, q,分别表示给出一颗n个节点的中序遍历满二叉树,后面有q个询问; 接下来有q组形 ...

  9. Spring Boot Autowirted注入找不到Bean对象解决方法

    报错:Consider defining a bean of type 'xxxxxxxxxxxxx' in your configuration 1. 你应该在 ApplyApplication 启 ...

  10. Nginx实现负载均衡(nginx + tomcat应用分布式)

    一.       工具   nginx-1.8.0 apache-tomcat-6.0.33 二.    目标   实现高性能负载均衡的Tomcat集群: 三.    步骤   1.首先下载Nginx ...