POJ

来源:互联网 发布:网络群发器 编辑:程序博客网 时间:2024/06/07 12:26

题目链接:http://poj.org/problem?id=2104点击打开链接

题目链接:http://poj.org/problem?id=2761点击打开链接

K-th Number
Time Limit: 20000MS Memory Limit: 65536KTotal Submissions: 59682 Accepted: 20824Case 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 31 5 2 6 3 7 42 5 34 4 11 7 3

Sample Output

563

Hint

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

Source

Northeastern Europe 2004, Northern Subregion

Feed the dogs
Time Limit: 6000MS Memory Limit: 65536KTotal Submissions: 21358 Accepted: 6773

Description

Wind loves pretty dogs very much, and she has n pet dogs. So Jiajia has to feed the dogs every day for Wind. Jiajia loves Wind, but not the dogs, so Jiajia use a special way to feed the dogs. At lunchtime, the dogs will stand on one line, numbered from 1 to n, the leftmost one is 1, the second one is 2, and so on. In each feeding, Jiajia choose an inteval[i,j], select the k-th pretty dog to feed. Of course Jiajia has his own way of deciding the pretty value of each dog. It should be noted that Jiajia do not want to feed any position too much, because it may cause some death of dogs. If so, Wind will be angry and the aftereffect will be serious. Hence any feeding inteval will not contain another completely, though the intervals may intersect with each other. 

Your task is to help Jiajia calculate which dog ate the food after each feeding. 

Input

The first line contains n and m, indicates the number of dogs and the number of feedings. 

The second line contains n integers, describe the pretty value of each dog from left to right. You should notice that the dog with lower pretty value is prettier. 

Each of following m lines contain three integer i,j,k, it means that Jiajia feed the k-th pretty dog in this feeding. 

You can assume that n<100001 and m<50001. 

Output

Output file has m lines. The i-th line should contain the pretty value of the dog who got the food in the i-th feeding.

Sample Input

7 21 5 2 6 3 7 41 5 32 7 1

Sample Output

32


下午刚学的主席树。。作为教学模板题照着博客打一遍 

好巧妙的结构。。

加了些注释表示自己的理解 主要目的是加深自己对主席树的理解 可能有不正确的地方 欢迎指正

需要具体形象化的理解请点击:http://blog.csdn.net/xuejye/article/details/77585518 点击打开链接

最好能够理解完构造过程再学习代码

另外因为该版本的代码网上较多 因此不说原代码借鉴地址(也找不到了)

#include <stdio.h>#include <algorithm>#define maxn 100010using namespace std;struct xjy{    int left;//储存儿子节点而非区间范围    int right;//同上    int num;//对于存在该节点的树 该节点所代表的区间的数字的个数};xjy tree[maxn*20];//树的空间创立int tot=0;//之后更新创立虚拟节点的节点下标int tot_num;//该区间不重复的值的个数 同时也是每一个线段树的叶子结点个数int n,m;int b[maxn],c[maxn],t[maxn];//b数组记录序列个数 c数组对其排序 t【i】表示主席树中所建立的第i个数的根节点的索引int build(int left,int right)//与线段树的构建类似 区别在于节点的创立不是使用线段树的倍数关系 而是逐个累加 这样方便了储存儿子节点的位置 也方便了接下来构造主席树 {    int root=++tot;    tree[root].num=0;    if(left=right)        return root;    int mid=(left+right)>>1;    tree[root].left=build(left,mid);    tree[root].right=build(mid+1,right);    return root;}//构造的此空树是主席树的最初形态 每个叶子节点储存的是从小到大排序后每个位置上数字的个数 即表示第i小的数有几个(注意要在所包含此节点的树中)//而第j个树表示区间1~j 其构造为1~j区间情况的线段树int update(int root,int x){    int now=++tot;//模拟中新创立的空节点 实际空间已经存在    int tmp=now;//记录新创立的线段树的根节点 之后返回根节点的数值 因为这需要与后面的t[]数组形成区间1~i的树的根节点为t[i] 即此时的tmp    tree[tot].num=tree[root].num+1;//此时新创立的根节点的值等于上一个创立的树的根节点的值+1(因为每次只更新一个位置的值+1 因此总体也只+1    int left=1;int right=tot_num;//对上一颗树进行扫描 寻找需要更改增加的节点和无需更改的节点    while(left<right)    {        int mid=(left+right)>>1;        if(x<=mid)//如果需要更改的值在左子树        {            tree[now].right=tree[root].right;//此时新创立的节点的右子树就是此时扫描点的右子树 因为无需更改 直接将新创立节点的右子树指向扫描点的右子树 这也是主席树精华所在(个人认为) 很大程度上节约了空间            tree[now].left=++tot;//在当前的新节点上对左子树创立一个新节点 此时当前新节点已经完成 递归进入下一个新节点            root=tree[root].left;//扫描的点进行与上面同样的动作 往左扫描            now=tot;//此时的新节点变为了当前新节点的左节点 然后递归            right=mid;//二分步骤        }        else        {                                   //类比左子树            tree[now].left=tree[root].left;            tree[now].right=++tot;            root=tree[root].right;            now=tot;            left=mid+1;        }        tree[now].num=tree[root].num+1;//对于新节点 即已经判断需要更改的节点在此区间 对其值进行更新 同样只需要+1    }    return tmp;//返回之前记录的这个新创立的线段树的根节点}int query(int qleft,int qright,int k)//qleft为1~i这个线段树的根节点 qright为1~j的 (假设查询【i~j】区间)(i~j区间的线段树可表示为 线段树j-1减去线段树((i-1)-1)类比树状数组思想 而接下来的操作都是在两个线段树的相加减基础上的 之所以可以相加减 因为主席树里的线段树保证了结构相同的特点{    int left=1;int right=tot_num;    while(left<right)    {        int mid=(left+right)>>1;        if(tree[tree[qright].left].num-tree[tree[qleft].left].num>=k)//如果左子树的值大于等于 说明第k大的树在左子树中        {            qleft=tree[qleft].left;            qright=tree[qright].left;            right=mid;        }        else//否则在右子树中        {            left=mid+1;            k-=(tree[tree[qright].left].num-tree[tree[qleft].left].num);            qleft=tree[qleft].right;            qright=tree[qright].right;        }    }    return left;//对相减的线段树进行扫描寻找第k大的值 直到找到 返回的值注意不是数本身 是第k大的数的下标 之后再在已经排序的c数组里得到这个数的具体值}int main(){    int n,m;    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)    {        scanf("%d",&b[i]);        c[i]=b[i];    }    sort(c+1,c+1+n);//先排序    tot_num=unique(c+1,c+n+1)-c-1;//后去重 这里之所以不用set容器实现 因为操作运算符里“-”不允许对其地址的加减操作(差不多这么解释。。 迭代器的东西跟操作运算符不是太懂 本来写的lower_bound()-set.begin()就是会报错运算符)    t[0]=build(1,tot_num);//先是基础树(num都为0)    for(int i=1;i<=n;i++)    {        t[i]=update(t[i-1],lower_bound(c+1,c+tot_num+1,b[i])-c);//每个树在上一个树的基础上进行更新和扫描    }    while(m--)    {        int l,r,k;        scanf("%d%d%d",&l,&r,&k);        printf("%d\n",c[query(t[l-1],t[r],k)]);//最后query返回的是c数组位置的下标索引     }}//最后注意几个点//1.主席树里每一颗线段树都有其相同的地方 而相同的地方只需要一个空间储存 保证了节约空间的目的//2.主席树的叶子结点从左到右为有序的 即从小到大 而更新的时候是对题目所给序列的从左到右更新


无注释版

#include <stdio.h>#include <algorithm>#define maxn 100010using namespace std;struct xjy{    int left;    int right;    int num;};xjy tree[maxn*20];int tot=0;int tot_num;int n,m;int b[maxn],c[maxn],t[maxn];int build(int left,int right){    int root=++tot;    tree[root].num=0;    if(left=right)        return root;    int mid=(left+right)>>1;    tree[root].left=build(left,mid);    tree[root].right=build(mid+1,right);    return root;}int update(int root,int x){    int now=++tot;    int tmp=now;    tree[tot].num=tree[root].num+1;    int left=1;int right=tot_num;    while(left<right)    {        int mid=(left+right)>>1;        if(x<=mid)        {            tree[now].right=tree[root].right;            tree[now].left=++tot;            root=tree[root].left;            now=tot;            right=mid;        }        else        {            tree[now].left=tree[root].left;            tree[now].right=++tot;            root=tree[root].right;            now=tot;            left=mid+1;        }        tree[now].num=tree[root].num+1;    }    return tmp;}int query(int qleft,int qright,int k){    int left=1;int right=tot_num;    while(left<right)    {        int mid=(left+right)>>1;        if(tree[tree[qright].left].num-tree[tree[qleft].left].num>=k)        {            qleft=tree[qleft].left;            qright=tree[qright].left;            right=mid;        }        else        {            left=mid+1;            k-=(tree[tree[qright].left].num-tree[tree[qleft].left].num);            qleft=tree[qleft].right;            qright=tree[qright].right;        }    }    return left;}int main(){    int n,m;    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)    {        scanf("%d",&b[i]);        c[i]=b[i];    }    sort(c+1,c+1+n);    tot_num=unique(c+1,c+n+1)-c-1;    t[0]=build(1,tot_num);    for(int i=1;i<=n;i++)    {        t[i]=update(t[i-1],lower_bound(c+1,c+tot_num+1,b[i])-c);    }    while(m--)    {        int l,r,k;        scanf("%d%d%d",&l,&r,&k);        printf("%d\n",c[query(t[l-1],t[r],k)]);    }}