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;
}

  

时间: 2024-10-09 20:41:11

POJ 2761 Feed the dogs(树状数组求区间第K大)的相关文章

【POJ2104】【整体二分+树状数组】区间第k大

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

树状数组求区间最值

树状数组求区间最值 树状数组(Binary Index Tree)利用二进制的一些性质巧妙的划分区间,是一种编程,时间和空间上都十分理想的求区间和的算法,同样我们可以利用树状数组优美的区间划分方法来求一个序列的最值 约定以 num[]  表示原数组, 以 idx[] 表示索引数组, Lowbit(x)=x&(-x) 树状数组求和时通过构造数组 idx[] 使 idx[k]=sum(num[tk]), tk [k-Lowbit(k)+1,k], 使用同样的方法构造最值索引数组: 以最大值为例, 先

树状数组求区间最大值

------  一直用 线段树 求区间最大值,想换种思路,用树状数组试试,肯定是可以的. 首先要对 树状数组的每个 i 所管理的区间有一定的理解.详见上篇博客: 树状数组(BIT)

【BZOJ1012】【树状数组求区间最值】最大数maxnumber

Description 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度. 2. 插入操作.语法:A n 功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾.限制:n是非负整数并且在长整范围内.注意:初始时数列是空的,没有一个数. Input 第一行两个整数,M和D,其中M表示操作

树状数组求区间最大值(树状数组)(复习)

如题. 当遇到单点更新时,树状数组往往比线段树更实用. 算法: 设原数序列为a[i],最大值为h[i](树状数组). 1.单点更新: 直接更新a[i],然后再更新h[i].若h[i]的值有可能改变的,则表示区间一定包含i结点.那么就两层lowbit更新所有可能的h. 单点更新时间复杂度O(logn*logn) 1 void update(int x) 2 { 3 while(x<=n) 4 { 5 h[x]=a[x]; 6 for(int i=1;i<lowbit(x);i<<=1

主席树套树状数组 动态区间第k小

先打上代码以后更新解释 1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 #include <cmath> 6 #define REP(i, s, n) for(int i = s; i <= n; i ++) 7 #define RAP(i, n, s) for(int i = n; i >= s; i --

POJ 3321 Apple Tree 【树状数组+建树】

题目链接:http://poj.org/problem?id=3321 Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 34812 Accepted: 10469 Description There is an apple tree outside of kaka's house. Every autumn, a lot of apples will grow in the tree. Kaka like

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 3321:Apple Tree(树状数组,提高题)

Apple Tree Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 18623   Accepted: 5629 Description There is an apple tree outside of kaka's house. Every autumn, a lot of apples will grow in the tree. Kaka likes apple very much, so he has been