zoj2112

/* 
 zoj 2112 
 线段树好题!!!
 这题和poj2104不同的是要修改,修改的时候在区间里用
 二分的方法找到和被修改的数相同的那个数,然后再用
 二分法找到修改之后的数的正确位置。

欢迎各位编程爱好者加我,最近被不少编程题困住了!希望得到各位的帮助
*/

#include <stdio.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 50001;
              
struct node{
 int l, r;
}seg[N * 3];
int a[N], tree[20][N], n;
void build(int id, int l, int r, int dep){
 seg[id].l = l;
 seg[id].r = r;
 if(l < r){
  int mid = (l + r) / 2;
  build(2 * id + 1, l, mid, dep + 1);
  build(2 * id + 2, mid + 1, r, dep + 1);
  //归并
  int ll, rr, p;
  ll = l;
  rr = mid + 1;
  p = l;
  while(p <= r){
   if(rr > r || (ll <= mid && tree[dep + 1][ll] <= tree[dep + 1][rr])){
    tree[dep][p] = tree[dep + 1][ll];
    p++;
    ll++;
   }else{
    tree[dep][p] = tree[dep + 1][rr];
    p++;
    rr++;
    //printf("r: %d/n", tree[dep][p - 1]);
   }
  }
 }else if(l == r){
  tree[dep][r] = a[r];
 }
}

void output(int id, int dep){
 printf("(%d, %d):", seg[id].l, seg[id].r);
 for(int j = seg[id].l; j <= seg[id].r; j++)
  printf("%d ", tree[dep][j]);
 printf("/n");
 if(seg[id].l < seg[id].r){
  output(2 * id + 1, dep + 1);
  output(2 * id + 2, dep + 1);
 }
}

int check(int id, int key, int l, int r, int dep){
 if(seg[id].l == l && seg[id].r == r){
  //找比key小的数的个数
  if(key <= tree[dep][l])
   return 0;
  if(key > tree[dep][r])
   return r - l + 1;
  int low = l, high = r, mid;
  //找第一个不比key小的数的位置
  while(low <= high){
   mid = (low + high) / 2;
   if(tree[dep][mid] < key)
    low = mid + 1;
   else
    high = mid - 1;
  }
  return low - l;
 }else{
  int mid = (seg[id].l + seg[id].r) / 2;
  int ans = 0;
  if(l <= mid) ans += check(2 * id + 1, key, l, min(mid, r), dep + 1);
  if(r > mid) ans += check(2 * id + 2, key, max(mid + 1, l), r, dep + 1);
  return ans;
 }
}


int query(int k, int l, int r){
 int low, high, mid, num;
 low = 1;
 high = n;
 k--; 
 while(low <= high){
  mid = (low + high) / 2;
  num = check(0, tree[0][mid], l, r, 0);
  //printf("key: %d   num: %d/n", tree[0][mid], num);
  if(num <= k)
   low = mid + 1;
  else
   high = mid - 1;
 }
 return tree[0][low - 1];
 return 0;
}

void renew(int id, int c, int b, int dep){
 if(seg[id].l <= c && c <= seg[id].r){
  //找到一个和a[c]相等的数,将其改为b
  int low, high, mid;
  low = seg[id].l;
  high = seg[id].r;
  while(low <= high){
   mid = (low + high) / 2;
   if(tree[dep][mid] < a[c])
    low = mid + 1;
   else
    high = mid - 1;
  }
  //tree[dep][low] = b;
  int g = low;
  if(b < tree[dep][low]){
   low = seg[id].l;
   high = g - 1;
   //找到第一个比b大的数
   while(low <= high){
    mid = (low + high) / 2;
    if(tree[dep][mid] < b)
     low = mid + 1;
    else
     high = mid - 1;
   }
   for(int j = g - 1; j >= low; j--)  tree[dep][j + 1] = tree[dep][j];
   tree[dep][low] = b;
  }else if(b > tree[dep][low]){
   low = g + 1;
   high = seg[id].r;
   while(low <= high){
    mid = (low + high) / 2;
    if(tree[dep][mid] < b)
     low = mid + 1;
    else
     high = mid - 1;
   }
   low--;
   for(int j = g; j < low; j++)  tree[dep][j] = tree[dep][j + 1];
   tree[dep][low] = b;
  }
  mid = (seg[id].l + seg[id].r) / 2;
  if(c <= mid)
   renew(2 * id + 1, c, b, dep + 1);
  else
   renew(2 * id + 2, c, b, dep + 1);
 }
}


int main(){
 int m, i;
 int l, r, k;
 int c, b, t;
 char op;
 scanf("%d", &t);
 while(t--){
  scanf("%d%d", &n, &m);
  for(i = 1; i <= n; i++)
   scanf("%d", &a[i]);
  build(0, 1, n, 0);
  //output(0, 0);
  //printf("/n");
  for(i = 1; i <= m; i++){
   cin >>op;
   if(op == 'Q'){
    scanf("%d%d%d", &l, &r, &k);
    printf("%d/n", query(k, l, r));
   }else if(op == 'C'){
    scanf("%d%d", &c, &b);
    renew(0, c, b, 0);
    a[c] = b;
    //output(0, 0);
   }
  }
 }
 return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值