hdu 4417 Super Mario 划分树(线段树)

看了大牛的思路http://blog.csdn.net/fp_hzq/article/details/8010322#comments

每次询问区间内小于等于k的数有几个,很明显符合划分树的范畴,然后只要改改query函数,没次对于大于中间的数就加上划分到左边的个数,否则不管。注意了:就是可能区间会划分到空,还有可能只有一个元素,而且等于查找的数,要特殊处理下

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define M 100001
#define md(x, y) (((x)+(y))>>1)
int sorted[M];       // 对原来集合中的元素排序后的值。
struct node {
    int val[M];      // val 记录第 k 层当前位置的元素的值
    int num[M];      // num 记录元素所在区间的当前位置之前进入左孩子的个数
}t[20];
int n,m,k;
void build(int lft, int rht, int p) {
    if(lft == rht) return;
    int i, mid = md(lft, rht);
    int isame = mid - lft + 1, same = 0;
    for(i = lft; i <= rht; i++)
        if(t[p].val[i] < sorted[mid]) isame--;
    int ln = lft, rn = mid + 1;
    for(i = lft; i <= rht; i++) {
        if(i == lft) {            // 初始一个子树。
            t[p].num[i] = 0;
        } else {                // 初始区间下一个节点。
            t[p].num[i] = t[p].num[i-1];
        }
        if(t[p].val[i] < sorted[mid]) {
            t[p].num[i]++;
            t[p+1].val[ln++] = t[p].val[i];
        }else if(t[p].val[i] > sorted[mid]) {
            t[p+1].val[rn++] = t[p].val[i];
        }else {
            if(same < isame) {
                same++;
                t[p].num[i]++;
                t[p+1].val[ln++] = t[p].val[i];
            }else {
                t[p+1].val[rn++] = t[p].val[i];
           }
        }
     }
     build(lft, mid, p+1);
     build(mid+1, rht, p+1);
}
int ans;
void query(int a, int b, int k, int p, int lft, int rht) {
    if(a>b)
    return;
    if(lft == rht)//只有一个元素,而且等于查找的数,要特殊处理下
    {
        //printf("p=%d\n",t[p].val[a]);
        if(t[p].val[a]<=k)
        {
            ans++;
            return;
        }
    }
    int s, ss, b2, bb, mid = md(lft, rht),sss;
    if(a == lft) {
        s = t[p].num[b];
        ss = 0;
    } else {
        s = t[p].num[b] - t[p].num[a-1];
        ss = t[p].num[a-1];
    }
    if(sorted[mid]>k) {    // 进入左孩子,同时更新区间端点值。
        a = lft + ss;
        b = lft + ss + s - 1;
         query(a, b, k, p+1, lft, mid);
    } else {
        //printf("a=%d\n",a);
        ans+=s;
        bb = a - lft - ss;
        b2 = b - a + 1 - s;
        a = mid + bb + 1;
        b = mid + bb + b2;//如果b2=0时,a>b,即区间会划分到空,下次递归跳出
        query(a, b, k,p+1, mid+1, rht);
     }
 }
 int main()
 {
     int i,j,x,y;
     while(scanf("%d%d",&n,&m)!=EOF)
     {
         for(i=1;i<=n;i++)
         {
             scanf("%d",&t[1].val[i]);
             sorted[i]=t[1].val[i];
         }
         sort(sorted+1,sorted+1+n);
         build(1,n,1);
         while(m--)
         {
             ans=0;
             scanf("%d%d%d",&x,&y,&k);
             query(x,y,k,1,1,n);
             printf("%d\n",ans);
         }
     }
     return 0;
 }


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值