zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", 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
    ",query(0,n-1,l,r,k,0,1));
            }
        }
        return 0;
    }
    

      

  • 相关阅读:
    一天一个 Linux 命令(44):ifstat 命令
    Java集合框架示意图
    Java中String类常见问题汇总
    一天一个 Linux 命令(43):netstat 命令
    Windows系统下,如何设置maven字符编码
    Java文件操作编程
    Java 注解(Annotation)
    Linux Centos7.4 更新Java jdk版本
    Java基础(6)Java数据类型扩展
    Windows系统下Elasticsearch7.15.2单服务器配置多节点
  • 原文地址:https://www.cnblogs.com/tenlee/p/4564505.html
Copyright © 2011-2022 走看看