动态区间第K大(树状数组+主席树)


很早以前做静态第K大的时候听到要用树套树就过于害怕逃走了,现在用分块暴力过了之后又想用树套树A一遍,于是就写了一下
starkmal的线段树+Splay常数卡出翔惹


  • 其实如果用主席树写了静态第k大就立即做这道题的话应该立即想到用主席树搞废话
  • 但是立即就能发现的问题就是定点修改之后我们需要将其后面的所有版本都修改一遍,不优秀,所以用树状数组维护版本信息。(仔细想想,这正好是树状数组的特性,记住修改节点含义)
  • 这里 1 <= n, m, A k A_k Ak <= 50000, 玄学oj上10个点时间接近3800ms
  • 没有离散化
#prag\
ma GCC optimize("O3") 
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#define mid (l+r>>1)
#define mid_1 ((l+r>>1)+1)
#define R(XXX) scanf("%d",&XXX)
#define DR(XXX,YYY) scanf("%d%d",&XXX,&YYY)
#define TR(XXX,YYY,ZZZ) scanf("%d%d%d",&XXX,&YYY,&ZZZ)
#define P(XXX) printf("%d\n",XXX)
#define lowbit(i) (i&-i)
using namespace std;
const int N = 50005;

int a[N], n, m, tot, Max, _0, _1, _2;

char cmd;
int ver[N], c[26000010], v, d;
int xx[N], yy[N], ls[26000010], rs[26000010];


void update( int &p, int l, int r ){
	if( ! p ) p = ++ tot; c[p] += d;
	if( l == r ) return;
	if( v <= mid ) update( ls[p], l, mid );
	else update( rs[p], mid_1, r );
}

void modify(){
	DR( _0, _1 );
	d = -1; v = a[_0];
	for( int i = _0; i <= n; i += lowbit(i) )
		update( ver[i], 0, N );
	d = 1; v = _1;
	for( int i = _0; i <= n; i += lowbit(i) )
		update( ver[i], 0, N );
	a[_0] = _1;
}


int query(){
	TR( _0, _1, _2 ); _0 --;
	int x = 0, y = 0, tmp;
	for( int i = _1; i; i ^= lowbit( i ) ) xx[x++] = ver[i];
	for( int i = _0; i; i ^= lowbit( i ) ) yy[y++] = ver[i];
	int l = 0, r = N;
	while( l < r ){
		tmp = 0;
		for( int i = 0; i < x; i ++ ) tmp += c[ls[xx[i]]];
		for( int i = 0; i < y; i ++ ) tmp -= c[ls[yy[i]]];
		if( _2 <= tmp ){
			for( int i = 0; i < x; i ++ ) xx[i] = ls[xx[i]];
			for( int i = 0; i < y; i ++ ) yy[i] = ls[yy[i]];
			r = mid;
		}
		else{
			for( int i = 0; i < x; i ++ ) xx[i] = rs[xx[i]];
			for( int i = 0; i < y; i ++ ) yy[i] = rs[yy[i]];
			_2 -= tmp; l = mid_1;
		}
	}
	return l;
}


int main()
{
//	freopen("tt.in","r",stdin);
	DR( n, m );
	d = 1;
	for( int i = 1; i <= n; i ++ ){ R(a[i]); v = a[i];
		for( int j = i; j <= n; j += lowbit(j) )
			update( ver[j], 0, N ); }
	while( m -- ){
		cin >> cmd;
		if( cmd == 'C' ) modify();
		else P( query() );
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值