标签:
| Time Limit: 20000MS | Memory Limit: 65536K | |
| Total Submissions: 40920 | Accepted: 13367 | |
| Case Time Limit: 2000MS | ||
Description
Input
Output
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
算法及题目分析:给你一个具有n个数字的数组,进行m此询问。每次询问,都会给你一个子区间,问你这个区间内的第k大的数字是多少?例如:数组序列(下标从1开始)是:1 5 2 6 3 7 4, 询问:[2, 5]的第3大的数字。[2, 5]区间的数是:5 2 6 3,排序后:2 3 5 6,第3大的数应该是5. 但是当数据量比较庞大的时候,询问次数比较多的时候,这种常规算法思想实现起来之星效率比较低。于是一种树型结构算法应运而生:【划分树算法】(基于线段树思想)(划分树实现细节有待补充)。【可以参考:程序设计 解题策略 吴永辉 王建德 编著 P2】
代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <string>
#include <iostream>
#include <algorithm>
#define N 100000+10
using namespace std;
//POJ 2104
int tree[50][N];//tree[p][i]表示第p层中第i的位置的值
int sorted[N];
int toleft[50][N];//toleft[p][i]表示第p层从1到i中有
//多少个数被划分入下一层的左子区间
//划分树建树的实现过程
void build(int ll ,int r, int dep)
{
    if(ll==r)
        return; //递归出口 若划分至叶子,则回溯
    int mid=(ll+r)>>1; //等于(l+r)/2 计算区间的中间指针
    int same=mid-ll+1; //计算[l,r]被分入下层左区间的个数
    for(int i=ll; i<=r; i++)
    {
        if(tree[dep][i] < sorted[mid])
            same--;
    }
    int llpos=ll;
    int rpos=mid+1;
    for(int i=ll; i<=r; i++)
    {
        if(tree[dep][i] < sorted[mid])
            tree[dep+1][llpos++]=tree[dep][i];
        else if(tree[dep][i]==sorted[mid] && same>0 )
        {
            tree[dep+1][llpos++]=tree[dep][i];
            same--;
        }
        else
            tree[dep+1][rpos++]=tree[dep][i];
        toleft[dep][i]=toleft[dep][ll-1] +llpos-ll;
    }
    build(ll, mid, dep+1);//递归计算下一层的左子区间
    build(mid+1, r, dep+1);//递归计算下一层的右子区间
}
int query(int L, int R, int ll, int r, int dep, int k)
//从划分数的dep层出发 自上而下的在大区间[L,R]里查询子区间
//[ll,r]中第K大的数
{
    if(ll==r)
        return tree[dep][ll];
    int mid=(L+R)>>1;
    int cnt=toleft[dep][r]-toleft[dep][ll-1];
    if(cnt >= k)
    {
        int newll=L+toleft[dep][ll-1]-toleft[dep][L-1];
        int newr = newll+cnt-1;
        return query(L, mid, newll, newr, dep+1, k);
    }
    else
    {
        int newr=r+toleft[dep][R]-toleft[dep][r];
        int newll=newr-(r-ll-cnt);
        return query(mid+1, R, newll, newr, dep+1, k-cnt);
    }
}
int main()
{
    int n, m;
    int i;
    while(~scanf("%d %d", &n, &m))
    {
        for(i=1; i<=n; i++)//n
        {
            scanf("%d", &tree[0][i]);//
            sorted[i]=tree[0][i];
        }
        sort(sorted+1,sorted+n+1);
        build(1, n, 0);//建立划分树
        while(m--)
        {
            int u, v, w;
            scanf("%d %d %d", &u, &v, &w);
            printf("%d\n", query(1, n, u, v, 0, w));
        }
    }
    return 0;
}
POJ2104 K-th Number (子区间内第k大的数字)【划分树算法模板应用】
标签:
原文地址:http://www.cnblogs.com/yspworld/p/4515621.html