[Spoj]MKTHNUM 可持久化线段树

看过一些论文,之后YY了好久……十一几天玩的实在无聊了突然想起来就把这题做了,居然是1A真是感动……

/*
  有一种线段树搞全局k大值的办法:
    先排序离散化之后维护区间数字出现次数,如果左子树数字总数多于k就在左子树找,不然在右子树找排名第(k-左子树和)的数……
  改成区间的话,有一种办法就是每个点来一棵树,第x棵树表示从1到x维护上面所说的线段树
  类似前缀和,这样两棵树做差就是目标区间维护每个数字出现次数的线段树
  考虑x到x+1棵树只需要变一个叶子的值,所以只要进行一次修改
  而一次修改只改了树从根到叶子的一条路径所以这两棵树大部分点可以共用,可以避免空间浪费
  具体做法,每次修改的时候先复制一下当前点,递归修改要修改的节点后返回新点……这应该就是可持久化了吧……
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef int SegT;

struct Node{int l,r,cnt;} node[5000000];
int nown=0,segt[100001];

SegT build(int l,int r) {
  int t=nown++,mid=l+r>>1;
  if (l==r) node[t]=(Node){0,0,0};
  else node[t]=(Node){build(l,mid),build(mid+1,r),0};
  return t;
}
SegT change(SegT t,int l,int r,int p,int k) {
  int tmp=nown++,mid=l+r>>1;
  if (l==r) {node[tmp]=(Node){0,0,node[t].cnt+k}; return tmp;}
  else if (p<=mid) node[tmp]=(Node){change(node[t].l,l,mid,p,k),node[t].r,0};
  else node[tmp]=(Node){node[t].l,change(node[t].r,mid+1,r,p,k),0};
  node[tmp].cnt=node[node[tmp].l].cnt+node[node[tmp].r].cnt;
  return tmp;
}
int select(SegT a,SegT b,int l,int r,int k) {
  a=segt[a-1],b=segt[b];
  for (int mid=l+r>>1; l<r; mid=l+r>>1) {
    if (node[node[b].l].cnt-node[node[a].l].cnt>=k) {a=node[a].l; b=node[b].l; r=mid;}
    else {k-=(node[node[b].l].cnt-node[node[a].l].cnt); a=node[a].r; b=node[b].r; l=mid+1;}
  }
  return l;
}

int a[100001],b[100001],n;
int get(int x) {
  int l=1,r=n+1;
  for (int mid=l+r>>1; l+1<r; mid=l+r>>1)
    if (x<b[mid]) r=mid; else l=mid;
  return l;
}

int main() {
  int m; scanf("%d%d",&n,&m);
  for (int i=1; i<=n; ++i) {scanf("%d",&a[i]); b[i]=a[i];};
  sort(&b[1],&b[n+1]);
  segt[0]=build(1,n);
  for (int i=1; i<=n; ++i) segt[i]=change(segt[i-1],1,n,get(a[i]),1);
  //for (int i=1; i<=n; ++i) cout << b[i] << ' ' << get(a[i]) << endl;
  //for (int i=0; i<=n; ++i) {check(segt[i],1,n); puts("");}
  while (m--) {
    int l,r,k;
    scanf("%d%d%d",&l,&r,&k);
    printf("%d\n",b[select(l,r,1,n,k)]);
  }
  //system("pause");
}

唉……荒废太久了……

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值