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 k-th Largest Group

代码:

//Author LJH
//www.cnblogs.com/tenlee
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#define clc(a, b) memset(a, b, sizeof(a))
using namespace std;

const int inf = 0x3f;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6+5;

struct Que
{
    int l, r, k, id;
    bool operator < (const Que &a) const 
    {
        if(l == a.l) return r < a.r;
        return l < a.l;
    }
}q[maxn];

struct Dog
{
    int val, id;
    bool operator < (const Dog &a) const 
    {
        return val < a.val;
    }
}d[maxn];

int node[maxn], ans[maxn], fval[maxn], pval[maxn], n, m;

inline int lowbit(int x)
{
    return x & -x;
}
void Init()
{
    clc(node, 0);
}

void add(int x, int val)
{
    //printf("x = %d\n", x);
    if(x == 0) return;
    while(x <= n)
    {
        node[x] += val;//记录这个结点下面包含了多少个数,即这个结点总共有多少个叶子结点
        x += lowbit(x);
    }
}

int findkth(int k)//查找第k大的数,返回第几个数,即排序后的下标
{
    int i, cnt = 0, ans = 0;
    for(i = 20; i >= 0; i--) //二进制思想
    {
        ans += (1 << i);
        if(ans >= n || cnt + node[ans] >= k) ////这里大于等于k的原因是可能大小为ans的数不在c[ans]的控制范围之内,所以这里求的是 < k
            ans -= (1 << i);
        else 
            cnt += node[ans];
        //cnt用来累加比当前ans小的总组数
    }
    //求出的ans是累加和(即小于等于ans的数的个数)小于k的情况下ans的最大值,所以ans+1就是第k大的数
    return ans + 1;
}

int main()
{
    int i, j;
    while(~scanf("%d %d", &n, &m))
    {
        Init();
        for(i = 1; i <= n; i++)
        {
            scanf("%d", &d[i].val);
            d[i].id = i;
        }
        sort(d+1, d+n+1);
        int k = 1;
        fval[d[1].id] = 1;
        pval[1] = d[1].val;
        for(i = 2; i <= n; i++)
        {
            if(d[i].val != d[i-1].val)
                pval[++k] = d[i].val; //消除重复的
            fval[d[i].id] = k; //记录这个数是排序后第几个数, 即原来的第几个数对应现在排序后的第几个数
        }
        for(i = 0; i < m; i++)
        {
            scanf("%d %d %d", &q[i].l, &q[i].r, &q[i].k);
            q[i].id = i;
            if(q[i].r < q[i].l) swap(q[i].l, q[i].r);
        }
        sort(q, q+m);

        int curl = 1, curr = 0; //左右 初始化左比右大
        for(i = 0; i < m; i++)
        {
            if(curr < q[i].l)// curl curr q[i].l q[i].r
            {
                for(j = curl; j <= curr; j++)//fval[j] 获得 原来第j个数在排序后的坐标
                    add(fval[j], -1); //清空上次查询,即把树状数组不属于该区间的设为0, 下同
                for(j = q[i].l; j <= q[i].r; j++)
                    add(fval[j], 1);
                curl = q[i].l; curr = q[i].r;
            }
            else // curl q[i].l curr q[i].r || q[i].l curl curr q[i].r
            {
                for(j = curl; j < q[i].l; j++) // 清空上次查询,就是把不属于对应区间的 值改为0
                    add(fval[j], -1);
                for(j = curr+1; j <= q[i].r; j++)
                    add(fval[j], 1);
                curl = q[i].l; curr = q[i].r;
            }
            ans[q[i].id] = pval[findkth(q[i].k)];
        }
        for(int i = 0; i < m; i++)
            printf("%d\n", ans[i]);
    }
    return 0;
}

划分树代码:

#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
const int MAXN=110000;
int tr[MAXN<<2];
int sorted[MAXN],toleft[20][MAXN],val[20][MAXN];

void build(int l, int r, int dep, int rt)
{
    if(l==r)
    {
        return;
    }
    int mid=(l+r)>>1;
    int lnum=mid-l+1;
    for(int i=l; i<=r; i++)
    {
        if(val[dep][i]<sorted[mid])
        {
            lnum--;
        }
    }
    int lp=l,rp=mid+1;
    int cur_lnum=0;
    for(int i=l; i<=r; i++)
    {
        if(i==l)
        {
            toleft[dep][i]=0;
        }
        else
        {
            toleft[dep][i]=toleft[dep][i-1];
        }
        if(val[dep][i]<sorted[mid])
        {
            toleft[dep][i]++;
            val[dep+1][lp++]=val[dep][i];
        }
        else if(val[dep][i]>sorted[mid])
        {
            val[dep+1][rp++]=val[dep][i];
        }
        else
        {
            if(cur_lnum<lnum)
            {
                cur_lnum++;
                toleft[dep][i]++;
                val[dep+1][lp++]=val[dep][i];
            }
            else
            {
                val[dep+1][rp++]=val[dep][i];
            }
        }
    }
    build(l,mid,dep+1,rt<<1);
    build(mid+1,r,dep+1,rt<<1|1);
}

int query(int l, int r, int L, int R, int k, int dep, int rt)
{
    if(l==r)
    {
        return val[dep][l];
    }
    int lnum,cur_lnum,rnum,cur_rnum;
    int mid=(l+r)>>1;
    if(l==L)
    {
        lnum=toleft[dep][R];
        cur_lnum=0;
    }
    else
    {
        lnum=toleft[dep][R]-toleft[dep][L-1];
        cur_lnum=toleft[dep][L-1];
    }
    if(lnum>=k)
    {
        int newL=l+cur_lnum;
        int newR=l+lnum+cur_lnum-1;
        return query(l,mid,newL,newR,k,dep+1,rt<<1);
    }
    else
    {
        int rnum=R-L+1-lnum;
        int cur_rnum=L-l-cur_lnum;
        int newL=mid+cur_rnum+1;
        int newR=mid+cur_rnum+rnum;
        return query(mid+1,r,newL,newR,k-lnum,dep+1,rt<<1|1);
    }
}

int main()
{
    int cas;
    int n, m;
    while(~scanf("%d %d", &n, &m))
    {
        for(int i=0; i<n; i++)
        {
            scanf("%d",&val[0][i]);
            sorted[i]=val[0][i];
        }
        sort(sorted,sorted+n);
        build(0,n-1,0,1);
        while(m--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            l--;
            r--;
            printf("%d\n",query(0,n-1,l,r,k,0,1));
        }
    }
    return 0;
}

  

posted @ 2015-06-09 20:32  豪气干云  阅读(282)  评论(0编辑  收藏  举报