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 = 100010;

struct Node
{
    int l,r;
    int len(){return r-l;}
    int mid(){return (l+r)/2;}
    bool in(int ll,int rr){return l>=ll && r<=rr;}
    void lr(int ll,int rr){l = ll; r=rr;}
}node[MAXN*4];

int num_left[20][MAXN],seg[20][MAXN],sa[MAXN];
//sa数组存sort后的结果
//seg数组存的是d层划分后的数字 (类似快排Partation (d-1)次后的结果)
//num_left存的是d层在i之前(包括i)小于 sa[mid] 的数的数目

void Init()
{
    //CLR(seg),CLR(num_left),CLR(node);
    		memset(seg,0,sizeof(seg));
		memset(num_left,0,sizeof(num_left));
		memset(node,0,sizeof(node));
}
void PaBuild(int t,int l, int r,int d)
{
    node[t].lr(l,r);
    if(node[t].len() == 0)return;
    int mid=(l+r)/2,lsame=mid-l+1;
    for(int i=l;i<=r;i++)//首先确定分到每一侧的数的数目
        if(seg[d][i] < sa[mid])//因为相同的元素可能被分到两侧
            lsame--;
    int lpos=l,rpos=mid+1;
    for(int i=l;i<=r;i++)
    {
        if(i == l)
            num_left[d][i]=0;
        else
            num_left[d][i]=num_left[d][i-1];
        if(seg[d][i] < sa[mid])
        {
            num_left[d][i]++;
            seg[d+1][lpos++]=seg[d][i];
        }
        if(seg[d][i] > sa[mid])
            seg[d+1][rpos++] = seg[d][i];
        if(seg[d][i] == sa[mid])
            if(lsame > 0)// 如果大于0,说明左侧可以分和sa[mid]相同的数字
            {
                lsame--;
                num_left[d][i]++;
                seg[d+1][lpos++]=seg[d][i];
            }
            else    //反之,说明左侧数字已经分满了,就分到右边去
                seg[d+1][rpos++]=seg[d][i];
    }
    PaBuild(t*2,l,mid,d+1);
    PaBuild(t*2+1,mid+1,r,d+1);
}
void Build(int s,int t)
{
    sort(sa+s,sa+t+s);
    PaBuild(1,s,t,1);
}

int find_rank(int t,int l,int r,int d,int val)//val指的是k
{
    if(node[t].len() == 0)return seg[d][l];
    int s,ss;   //s表示区间[l,r]有多少个小于sa[mid]的数被分到左边
    if( l == node[t].l)//ss表示从当前区间的L到l-1有多少个小于sa[mid]的数被分到左边,L,R指的是树上当前节点的区间范围
        ss=0;
    else
        ss=num_left[d][l-1];
    s=num_left[d][r]-ss;
    if(s>=val)
        return find_rank(t*2, node[t].l+ss,node[t].l+ss+s-1,d+1,val);
    else
    {
        int mid = node[t].mid();
        int bb=l-node[t].l-ss;  //表示从当前区间L到l-1有多少个分到右边
        int b=r-l+1-s;          //表示[l,r]有多少个分到右边
        return find_rank(t*2+1,mid+bb+1,mid+bb+b,d+1,val-s);
    }
}

int Query(int s,int t,int k)
{
    return find_rank(1,s,t,1,k);
}

int main()
{
    int n,m,x,y,k;

    while(~scanf("%d%d",&n,&m))
    {
        Init();
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&sa[i]);
            seg[1][i] = sa[i];
        }

        Build(1,n);
        while(m--)
        {
            scanf("%d%d%d",&x,&y,&k);
            int ans=Query(x,y,k);
            printf("%d\n",ans);
        }
    }
    return 0;
}

poj 2401 划分树 求区间第k大的数,布布扣,bubuko.com

时间: 2024-12-26 16:11:03

poj 2401 划分树 求区间第k大的数的相关文章

[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] 把快排的结果从上至下依次放入线段树,就构成了划分树,划分的意思就是选定一个数,把原序

hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)

http://acm.hdu.edu.cn/showproblem.php?pid=2665 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first line is the number of the test cases. For each test case, the first line contain two integer n and m (

poj 2104主席树求区间第k小

POJ - 2104 题意:求区间第k小 思路:无修改主席树 AC代码: #include "iostream" #include "iomanip" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector" #include "set&

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

题目链接:http://poj.org/problem?id=2104 主席树入门题目,主席树其实就是可持久化权值线段树,rt[i]维护了前i个数中第i大(小)的数出现次数的信息,通过查询两棵树的差即可得到第k大(小)元素. #include<cstdio> #include<vector> #include<algorithm> using namespace std; #define lson(i) node[i].lson #define rson(i) node

HDOJ题目4417 Super Mario(划分树求区间比k小的个数+二分)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3313    Accepted Submission(s): 1548 Problem Description Mario is world-famous plumber. His "burly" figure and amazing jumping a

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

poj2104 求区间第k大 可持久化线段树

poj2104 求区间第k大  可持久化线段树 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef

POJ2761---Feed the dogs (Treap求区间第k大)

题意 就是求区间第k大,区间 不互相包含. 尝试用treap解决一下 第k大的问题. 1 #include <set> 2 #include <map> 3 #include <cmath> 4 #include <ctime> 5 #include <queue> 6 #include <stack> 7 #include <cstdio> 8 #include <string> 9 #include <

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