题意:给一个数组,每次询问输出在区间[L,R]之间小于H的数字的个数。

此题可以使用划分树在线解决。

划分树可以快速查询区间第K小个数字。逆向思考,判断小于H的最大的一个数字是区间第几小数,即是答案。这一步可以使用二分搜索上界。时间复杂度是O(logn*logn)。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
#define MAXN 100005
using namespace std;
struct Divide_Tree
{
    ][MAXN];
    ][MAXN];
    void build(int c,int L,int R)
    {
        ,lsame=mid+-L,lp=L,rp=mid+;
        for(int i=L; i<mid; ++i)
            if(sorted[i]<sorted[mid])  lsame--;
        for(int i=L; i<=R; ++i)
        {
            ;
            ];
            if(dat[c][i]<sorted[mid])
            {
                dat[c+][lp++]=dat[c][i];
                toleft[c][i]++;
            }
            else if(dat[c][i]>sorted[mid])
                dat[c+][rp++]=dat[c][i];
            else
            {
                if(lsame)
                {
                    lsame--;
                    toleft[c][i]++;
                    dat[c+][lp++]=sorted[mid];
                }
                ][rp++]=sorted[mid];
            }
        }
        if(L==R) return ;
        build(c+,L,mid);
        build(c+,mid+,R);
    }
    int query(int c,int L,int R,int ql,int qr,int k)
    {
        if(L==R)   return  dat[c][L];
        ;
        int la,lb,ra,rb;
        ;
        ];
        lb=toleft[c][qr];
        ra=ql-L-la;
        rb=qr+-L-lb;
        int s=lb-la;
        ,L,mid,L+la,L+lb-,k);
        ,mid+,R,mid++ra,mid+rb,k-s);
    }
};
Divide_Tree tree;
int n;
int Bsearch(int low,int high,int key,int ql,int qr)
{
    )>>;
    while(low<high)
    {
        ,,n,ql,qr,mid)<=key) low=mid;
        ;
        mid=(low+high+)>>;
    }
    ,,n,ql,qr,mid)<=key) return mid;
    ;
}
int main()
{
    ;
    int q;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&q);
        ; i<=n; ++i)
        {
            scanf("%d",&tree.arr[i]);
            tree.sorted[i]=tree.dat[][i]=tree.arr[i];
        }
        sort(tree.sorted+,tree.sorted+n+);
        tree.build(,,n);
        int l,r,k;
        printf("Case %d:\n",++kase);
        while(q--)
        {
            scanf("%d%d%d",&l,&r,&k);
            l++;
            r++;
            ,r-l+,k,l,r);
            ) printf("0\n");
            else printf("%d\n",p);
        }
    }
    ;
}

另外,此题有更高效的算法。 未完待续。

此题也可以离线解决。

首先考虑单次查询整个区间小于某个数的数字个数的思路,我们可以统计每个数字出现的次数,然后利用前缀和快速计算小于该数的数字个数。

如果是查询部分区间[L,R]小于某数x的数字个数的话,答案为 区间[1,R]小于x的数字个数 减去 区间[1,L-1]小于x的数字个数。那么如何计算区间[1,S]内小于x的数字个数呢。

每出现一个数字v就在第v个位置加一即可。统计小于x的数字个数即计算前x个位置的和,这里需要求和,也用到了修改,显然用树状数组更高效。这样当枚举到第i个数字时,求区间[1,i]内小于x的数字个数即此时计算前x个数字的和。

由于这里数字较大,数组下标存不下,所以需要离散化。

 #include<iostream>
 #include<vector>
 #include<cstring>
 #include<map>
 #include<cstdio>
 #include<algorithm>
 #define MAXN 100005
 using namespace std;
 int n,m,cn;
 map<int,int> has;
 struct BIT
 {
     int dat[MAXN];
     void clear()
     {
         memset(dat,,sizeof(dat));
     }
     int lowbit(int x)
     {
         return -x&x;
     }
     void add(int x,int v)
     {
         while(x<=cn)
         {
             dat[x]+=v;
             x+=lowbit(x);
         }
     }
     int sum(int x)
     {
         ;
         )
         {
             s+=dat[x];
             x-=lowbit(x);
         }
         return s;
     }
 };
 struct Segment
 {
     int num,left,right,high;
     int presum,ans;
     Segment (int a,int b,int c,int d):num(a),left(b),right(c),high(d) {}
     bool operator < (const Segment &p) const
     {
         return left<p.left;
     }
 };
 bool cmp(Segment a,Segment b)
 {
     return a.num<b.num;
 }
 vector<Segment> vec;
 vector<int> numb;
 int arr[MAXN];
 vector<int> posL[MAXN],posR[MAXN];
 BIT tree;
 int main()
 {
     int T;
     scanf("%d",&T);
     ;
     while(T--)
     {
         scanf("%d%d",&n,&m);
         numb.clear();
         ; i<=n; ++i)
         {
             scanf("%d",&arr[i]);
             numb.push_back(arr[i]);
             posL[i+].clear();
             posR[i+].clear();
         }
         vec.clear();
         ; i<m; ++i)
         {
             int x,y,z;
             scanf("%d%d%d",&x,&y,&z);
             x++;
             y++;
             numb.push_back(z);
             vec.push_back(Segment (i,x,y,z));
         }
         sort(vec.begin(),vec.end());
         ; i<vec.size(); ++i)
         {
             posL[vec[i].left].push_back(i);
             posR[vec[i].right].push_back(i);
         }
         has.clear();
         cn=;
         sort(numb.begin(),numb.end());
         ; i<numb.size(); ++i)
             if(!has[numb[i]]) has[numb[i]]=++cn;
         tree.clear();
         ; i<=n; ++i)
         {
             ; j<posL[i].size(); ++j)
             {
                 int u=posL[i][j];
                 int v=has[vec[u].high];
                 vec[u].presum=tree.sum(v);
             }
             tree.add(has[arr[i]],);
             ; j<posR[i].size(); ++j)
             {
                 int u=posR[i][j];
                 int v=has[vec[u].high];
                 vec[u].ans=tree.sum(v)-vec[u].presum;
             }
         }
         sort(vec.begin(),vec.end(),cmp);
         printf("Case %d:\n",++kase);
         ; i<vec.size(); ++i)
             printf("%d\n",vec[i].ans);
     }
     ;
 }

离线的另一种思路。

我们可以按照每个数的大小顺序插入到树状数组中,同时按照高度的大小顺序查询。

这样将所有数和高度一起存入数组并从小到大排序。这样遇到数就在树状数组该数字的位置加一,遇到查询就对该区间求和,这样可以保证在查询的时候树状数组上被插入的数都是小于x的。

注意,排序的时候如果数的大小和查询高度大小一样,则查询放在后面。

#include<iostream>
#include<vector>
#include<cstring>
#include<map>
#include<cstdio>
#include<algorithm>
#define MAXN 100005
using namespace std;
int n,m;
struct BIT
{
    int dat[MAXN];
    void clear()
    {
        memset(dat,,sizeof(dat));
    }
    int lowbit(int x)
    {
        return -x&x;
    }
    void add(int x,int v)
    {
        while(x<=n)
        {
            dat[x]+=v;
            x+=lowbit(x);
        }
    }
    int sum(int x)
    {
        ;
        )
        {
            s+=dat[x];
            x-=lowbit(x);
        }
        return s;
    }
};
struct Segment
{
    int num,dat,left,right;
    int ans;
    Segment(,,,):num(a),dat(b),left(c),right(d) {}
    bool operator <(const Segment &p) const
    {
        return dat<p.dat||(dat==p.dat&&num>p.num);
    }
};
bool cmp(Segment a,Segment b)
{
    return a.num<b.num;
}
vector<Segment> vec;
BIT tree;
int main()
{
    ;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        vec.clear();
        ; i<=n; ++i)
        {
            int t;
            scanf("%d",&t);
            vec.push_back(Segment(i,t));
        }
        ; i<m; ++i)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            x++;
            y++;
            vec.push_back(Segment(-m+i,z,x,y));
        }
        sort(vec.begin(),vec.end());
        tree.clear();
        ; i<vec.size(); ++i)
        {
            ) tree.add(vec[i].num,);
            );
        }
        sort(vec.begin(),vec.end(),cmp);
        printf("Case %d:\n",++kase);
        ; i<vec.size(); ++i)
            ) printf("%d\n",vec[i].ans);
            else break;
    }
    ;
}

HDU 4417 - Super Mario ( 划分树+二分 / 树状数组+离线处理+离散化)的更多相关文章

  1. HDU 4417 Super Mario(划分树)

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  2. HDU 4417 Super Mario(划分树问题求不大于k的数有多少)

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  3. HDU 4417 Super Mario(划分树+二分)

    题目链接 #include <cstdio> #include <cstring> #include <algorithm> using namespace std ...

  4. HDU 4417 Super Mario(2012杭州网络赛 H 离线线段树)

    突然想到的节约时间的方法,感觉6翻了  给你n个数字,接着m个询问.每次问你一段区间内不大于某个数字(不一定是给你的数字)的个数 直接线段树没法做,因为每次给你的数字不一样,父节点无法统计.但是离线一 ...

  5. HDU 4417 Super Mario (划分树)(二分)

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  6. HDU 4417 Super Mario(主席树求区间内的区间查询+离散化)

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  7. hdu 4417 Super Mario/树套树

    原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 题意很简单,给定一个序列求一个区间 [L, R,]中小于等于H的元素的个数. 好像函数式线段树可 ...

  8. 主席树:HDU 4417 Super Mario

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. hdu 4417 Super Mario 树状数组||主席树

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Prob ...

随机推荐

  1. OA系统部门结构树

    public class DepartmentUtils { /** * @param topList 顶级部门列表 * @param removeId 删除部门的id * @return */ pu ...

  2. mac 下基于firebreath 开发多浏览器支持的浏览器插件

    mac 下基于firebreath 开发多浏览器支持的浏览器插件 首先要区分什么是浏览器扩展和浏览器插件;插件可以像本地程序一样做的更多 一. 关于 firebreath http://www.fir ...

  3. CAS原理全面分析

    http://blog.chinaunix.net/uid-22816738-id-3525939.html 上文对CAS各方面原理做了很详细.很明了分析,包括CAS架构.认证协议.安全性.登录.认证 ...

  4. GRUB4DOS入门

    目 录 第1章 GRUB4DOS入门    1 1.1 用途    1 1.2 安装    1 1.2.1 修改MBR    1 1.2.2 修改PBR    3 1.2.3 DOS    4 1.2 ...

  5. table合并单元格colspan和rowspan .

    colspan和rowspan这两个属性用于创建特殊的表格. colspan是“column span(跨列)”的缩写.colspan属性用在td标签中,用来指定单元格横向跨越的列数: 在浏览器中将显 ...

  6. Java并发编程:并发容器之ConcurrentHashMap

    转载: Java并发编程:并发容器之ConcurrentHashMap JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的 ...

  7. robotframework笔记5

    循环条件 *** Settings *** Library BuiltIn Library Collections *** Test Cases *** TestCase01 My Keyword 0 ...

  8. C++的vector学习abc

    开始学习和使用vector了,用到之后再去学似乎神迹的感觉啊,就像跑一下就能给个糖吃哈哈 百度上的六种初始化的方法就不再说了,那些方法都很对. 如果没有值初始化,系统会自行初始化,目前我遇到的是用脚标 ...

  9. SQL语句技巧(上个样式太差了)

      以下并非本人整理,但是看后感觉相当不错,特此分享. 1.应用程序中,保证在实现功能的基础上,尽量减少对数据库的访问次数:通过搜索参数,尽量减少对表的访问行数,最小化结果集,从而减轻网络负担:能够分 ...

  10. boost 源码编译 的 Makefile.am写法备份

    include $(top_srcdir)/common.mk bin_PROGRAMS= lib_LIBRARIES= lib_LTLIBRARIES= lib_LTLIBRARIES+=libSt ...