POJ 2761-Feed the dogs(划分树)求区间内第k小的数

Feed the dogs

Time Limit: 6000MS   Memory Limit: 65536K
Total Submissions: 17679   Accepted: 5561

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 2
1 5 2 6 3 7 4
1 5 3
2 7 1

Sample Output

3
2

Source

POJ Monthly--2006.02.26,zgl & twb

求区间内第k小的数。

和划分树经典的POJ 2104简直是一道题,直接复制代码交上去就AC了。。

/*
* Copyright (c) 2016, 烟台大学计算机与控制工程学院
* All rights reserved.
* 文件名称:k.cpp
* 作    者:单昕昕
* 完成日期:2016年4月15日
* 版 本 号:v1.0
*/
#include <iostream>
#include <stdio.h>
#include <algorithm>
const int maxn = 100005;
using namespace std;
int sor[maxn];//借助sort排序的数组
struct node
{
    int num[maxn];//当前层的数
    int cnt[maxn];
    //cnt[]数组是划分树的核心部分
    //保存每一个元素的左边的元素中位于下一层左子树的个数
} tree[40];//40是树的层数
//建树代码如下
void buildtree(int l, int r, int d)//d是深度
{
    if (l == r)//递归出口
    {
        return;
    }
    int mid = (l+r)>>1;//划分左右区间
    int opleft = l, opright = mid+1;//对左右子树的操作位置的初始化
    int same_as_mid = 0;//和sor[mid]相同的数的数目
    //计算在mid左边有多少个和sor[mid]相同的数(包括mid),都要放到左子树
    for (int i = mid; i > 0; i--)
    {
        if (sor[i] == sor[mid])
            same_as_mid++;
        else
            break;
    }
    int cnt_left = 0;//被划分到左子树的个数
    for (int i = l; i <= r; i++)
    {
        //从l到r开始遍历
        if (tree[d].num[i] < sor[mid])//左
        {
            tree[d+1].num[opleft++] = tree[d].num[i];
            cnt_left++;
            tree[d].cnt[i] = cnt_left;
        }
        else if(tree[d].num[i] == sor[mid] && same_as_mid)
        {
            //相同的都放在左子树
            tree[d+1].num[opleft++] = tree[d].num[i];
            cnt_left++;
            tree[d].cnt[i] = cnt_left;
            same_as_mid--;
        }
        else//右
        {
            tree[d].cnt[i] = cnt_left;
            tree[d+1].num[opright++] = tree[d].num[i];
        }
    }
    //递归建树
    buildtree(l, mid, d+1);
    buildtree(mid+1, r, d+1);
}
int query(int l, int r, int d, int ql, int qr, int k)
//1 n 0 a b k
//在d层[l,r]的节点里查找[a,b]中的第k小值
{
    if (l == r)//递归出口
        return tree[d].num[l];
    int mid = (l+r)>>1;
    int sum_in_left;//区间内元素位于下一层左子树的个数
    int left;//[l,ql-1]左边的元素中位于下一层左子树的个数
    if (ql == l)
    {//如果ql是节点的左边界则有cnt[qr]个数进入左子树
        sum_in_left = tree[d].cnt[qr];
        left = 0;
    }
    else
    {//如果ql不是节点的左边界则有cnt[qr]-cnt[ql-1]个数进入了左子树
        sum_in_left = tree[d].cnt[qr] - tree[d].cnt[ql-1];
        left = tree[d].cnt[ql-1];
    }
    if (sum_in_left >= k)
    {//要找的点在左子树
        //确定下一步询问的位置:
        //如果在ql的左边有left个进入左子树
        //那么ql到qr中第一个进入左子树的必定在l+left的位置
        int new_ql = l+left;
        int new_qr = new_ql+sum_in_left-1;
        return query(l, mid, d+1, new_ql, new_qr, k);
    }
    else//要找的点在右子树
    {
        //确定下一步询问的位置
        int a = ql - l - left;//表示当前区间左半部分即[l,ql-1]中在下一层是右孩子的个数
        int b = qr - ql + 1 - sum_in_left;//表示当前区间右半部分即[ql,qr]中在下一层是右孩子的个数
        int new_ql = mid + a + 1;
        int new_qr = mid + a + b;
        //k-sum_in_left表示要减去区间里已经进入左子树的个数
        return query(mid+1, r, d+1, new_ql, new_qr, k - sum_in_left);
    }
}
int main()
{
    int n,m,i,a,b,k;
    scanf("%d%d",&n,&m);
    for(i=1; i<=n; ++i)
    {
        scanf("%d",&sor[i]);//先插入到sor数组
        tree[0].num[i]=sor[i];//再插入第一层
    }
    sort(sor+1,sor+n+1);//升序排列
    buildtree(1,n,0);//建树
    for(i=1; i<=m; ++i)
    {//查询
        scanf("%d%d%d",&a,&b,&k);
        printf("%d\n",query(1,n,0,a,b,k));
    }
    return 0;
}
时间: 2024-12-28 01:13:31

POJ 2761-Feed the dogs(划分树)求区间内第k小的数的相关文章

【大杀器】利用划分树秒杀区间内第k大的数

最近看了一道题,大概就是给出一个序列,不断询问其子区间内第k大的数,下面是个截图 绕了一圈没找到中文版题目,if(你是大佬) then 去看截图:else{我来解释:给出一个整数n,和一个整数m,分别表示序列元素个数和询问次数,然后输入n个数和m个询问,每个询问包含3个数,分别是区间起止点(l和r)和k,求出区间内第k大的数并输出:}这是一道很简单的模板题,怎么解决呢?小编最初想到的是打暴力,正所谓暴力出奇迹,说不定可以成功,反正不会优化,先试试吧,直接把规定区间[l,r]排一次序了,然后在查找

POJ 2761 Feed the dogs

静态区间第K大,主席树.... Feed the dogs Time Limit: 6000MS   Memory Limit: 65536K Total Submissions: 15491   Accepted: 4780 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 Wi

poj 2401 划分树 求区间第k大的数

题目:http://poj.org/problem?id=2104 划分树待我好好理解下再写个教程吧,觉得网上的内容一般,,, 模板题: 贴代码: #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define CLR(a) memset(a,0,sizeof(a)) const int MAXN = 1000

[csu/coj 1080]划分树求区间前k大数和

题意:从某个区间内最多选择k个数,使得和最大 思路:首先题目给定的数有负数,如果区间前k大出现负数,那么负数不选和更大,于是对于所有最优选择,负数不会出现,所以用0取代负数,问题便转化为区间的前k大数和. 划分树: [1  6  3  8  5  4  7  2] [6  8  5  7][1  3  4  2] [8  7][6  5][3  4][1  2] [8][7][6][5][4][3][2][1] 把快排的结果从上至下依次放入线段树,就构成了划分树,划分的意思就是选定一个数,把原序

POJ 2761 Feed the dogs(树状数组求区间第K大)

题目链接: 戳我 题目大意:Jiajia要为宠物狗,宠物狗按成一排站好(1 < i <= n),第 i 只狗的喜欢程度是 a[i], 之后他会先喂某个区间内第k个 即 n 个数, m个询问,接着是 n个数 接下来 m 行,每行是 l r k即 l 到 r 这个区间第 k 小的数,每个询问输出一个答案,即 a[i] 求区间第k大有很多算法, 详见此博客 [数据结构练习] 求区间第K大数的几种方法 我用的树状数组解法,来自 树状数组从前往后求和,用来解第k大(或小)的数 poj 2985 The

Splay树(区间第k小)——POJ 2761 Feed the dogs

对应POJ题目:点击打开链接 Feed the dogs Time Limit: 6000MS   Memory Limit: 65536K Total Submissions: 16655   Accepted: 5203 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 Win

HDU 4417 Super Mario(主席树求区间内的区间查询+离散化)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5101    Accepted Submission(s): 2339 Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping abilit

POJ 2761 Feed the dogs (主席树)(K-th 值)

                                                            Feed the dogs Time Limit: 6000MS   Memory Limit: 65536K Total Submissions: 20634   Accepted: 6494 Description Wind loves pretty dogs very much, and she has n pet dogs. So Jiajia has to feed

HDU 3473-Minimum Sum(划分树-求区间sigma最小值)

Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3710    Accepted Submission(s): 852 Problem Description You are given N positive integers, denoted as x0, x1 ... xN-1. Then give you