[bzoj3224]普通平衡树 SBT+替罪羊树

##3224: Tyvj 1728 普通平衡树

Description

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入x数
  2. 删除x数(若有多个相同的数,因只删除一个)
  3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
  4. 查询排名为x的数
  5. 求x的前驱(前驱定义为小于x,且最大的数)
  6. 求x的后继(后继定义为大于x,且最小的数)

Input

第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

Output

对于操作3,4,5,6每行输出一个数,表示对应答案

Sample Input

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

Sample Output

106465
84185
492737

HINT

1.n的数据范围:n<=100000

2.每个数的数据范围:[-2e9,2e9]

暂时没决定模板

#include<cstdio>
struct SBT{
	int son[2],cnt,v,size;
}e[100005];
int n,cnt,root,ans;
inline void rotate( int &k, bool f ){
	int p = e[k].son[f]; e[k].son[f] = e[p].son[!f]; e[p].son[!f] = k;
	e[k].size = e[e[k].son[1]].size + e[e[k].son[0]].size + e[k].cnt;
	e[p].size = e[e[p].son[1]].size + e[e[p].son[0]].size + e[p].cnt; 
	k = p;
}
void maintain( int &k,bool f ){
    if(!k) return;
	if(e[e[k].son[!f]].size < e[e[e[k].son[f]].son[f]].size) 
		rotate(k, f);
    else if(e[e[k].son[!f]].size < e[e[e[k].son[f]].son[!f]].size) { 
		rotate(e[k].son[f], !f); rotate(k, f); 
	}
    else return;
    maintain(e[k].son[f],f); 
	maintain(k,f);
}
void insert( int &k, int x ){
	if(!k){ 
		k = ++cnt; 
		e[k].size = e[k].cnt = 1; 
		e[k].v = x; 
		return ; 
	}
	e[k].size++; 
	if( e[k].v == x ) { 
		e[k].cnt++; 
		return ; 
	}
	insert(e[k].son[e[k].v<x],x); 
	maintain(k,e[k].v<x);
}
int del( int &k, int x ){
	if(!k) return k; 
	int tmp;
	if( e[k].v == x ){
		if( e[k].cnt > 1 ){ 
			e[k].size--; e[k].cnt--; 
			return k; 
		}else if( !(e[k].son[0]*e[k].son[1]) ){ 
			k = e[k].son[0] + e[k].son[1]; 
		}else { 
			tmp = del(e[k].son[0],x+1); 
			e[k].cnt = e[tmp].cnt; 
			e[k].v = e[tmp].v; 
			e[k].size = e[e[k].son[1]].size + e[e[k].son[0]].size + e[k].cnt; 
			return k;
		}
	} else if( (e[k].v<x && !e[k].son[1])||(e[k].v>x && !e[k].son[0])){
		tmp=k;k=e[k].son[0];
		e[k].size = e[e[k].son[0]].size + e[e[k].son[1]].size + e[k].cnt;
		return tmp;
	}
	else tmp = del(e[k].son[e[k].v<x],x);
	e[k].size = e[e[k].son[0]].size + e[e[k].son[1]].size + e[k].cnt; 
	return tmp;
}
int rank( int k, int x ){
	if( k == 0 ) return 0;
	if( e[k].v == x ) return e[e[k].son[0]].size+1;
	else if( x > e[k].v ) return e[e[k].son[0]].size + e[k].cnt + rank(e[k].son[1],x);
	else return rank(e[k].son[0],x);
}
int kth( int k, int x ){
	if( k == 0 ) return 0;
	if( x <= e[e[k].son[0]].size ) return kth(e[k].son[0],x);
	else if( x > e[e[k].son[0]].size + e[k].cnt ) return kth(e[k].son[1],x-e[e[k].son[0]].size-e[k].cnt);
	else return e[k].v;
}
void pred( int k, int x ){
	if( k == 0 ) return ;
	if( e[k].v < x ){ 
		ans = e[k].v; 
		pred(e[k].son[1],x); 
	}
	else pred(e[k].son[0],x);
}
void succ( int k, int x ){
	if( k == 0 ) return ;
	if( e[k].v > x ){ 
		ans = e[k].v; 
		succ(e[k].son[0],x); 
	}
	else succ(e[k].son[1],x);
}
int main(){
	scanf("%d", &n);
    int opt,x;
    for( int i = 1; i <= n; i++ ){
        scanf("%d%d", &opt, &x);
        switch(opt) {
            case 1: insert(root,x); break;
            case 2: del(root,x); break;
            case 3: printf("%d\n",rank(root,x)); break;
            case 4: printf("%d\n",kth(root,x)); break;
            case 5: ans=0; pred(root,x); printf("%d\n",ans); break;
            case 6: ans=0; succ(root,x); printf("%d\n",ans); break;
        }
    }
    return 0;
}

7.26
替罪羊树是一棵子树不平衡,就暴力重建的树
时间很理想
是不用旋转的BST
因此可以套线段树

#include<bits/stdc++.h>
#define a 0.7
using namespace std;
const int inf = 0x7fffffff;
const int N = 1000000 + 5;
int n, m, q, sz, cnt, c[N][2], far[N], root, sum[N], siz[N], val[N], mx[N], dfn[N];
void init(){
	sz = 2; root = 1;
	val[1] = -inf; siz[1] = 2; c[1][1] = 2;
	val[2] = inf; siz[2] = 1; far[2] = 1;
}
bool check( int k ) { return (double)siz[k] * a >= (double)siz[c[k][0]] && (double)siz[k] * a >= (double)siz[c[k][1]]; }
void dfs( int k ){
	if( c[k][0] ) dfs( c[k][0] );
	dfn[++cnt] = k;//zheli k he cnt gaofanle
	if( c[k][1] ) dfs( c[k][1] );
}
int build( int l, int r ){
	if( l > r ) return 0;
	int mid = l + r >> 1, fa = dfn[mid];
	far[c[fa][0] = build(l, mid - 1)] = fa;
	far[c[fa][1] = build(mid + 1, r)] = fa;
	siz[fa] = siz[c[fa][0]] + siz[c[fa][1]] + 1;
	return fa;
}
void rebuild( int k ){
	cnt = 0; dfs(k);
	int y = far[k], lr = (k == c[y][1]);
	int now = build( 1, cnt );
	far[c[y][lr] = now] = y;
	if( k == root ) root = now;
}
void insert( int x ){
	int now = root, nd = ++sz, unv = 0;
	siz[nd] = 1; val[nd] = x;
	while( 1 ){
		siz[now]++;
		bool lr = ( x >= val[now] );
		if( c[now][lr] ) now = c[now][lr];
		else{ far[c[now][lr] = nd] = now; break; }
	}
	for( int i = nd; i; i = far[i] ) if( !check(i) ) unv = i;
	if( unv ) rebuild( unv );
}
int rank( int x ){
	int now = root, rk = 0;
	while( now ){
		if( val[now] < x ) rk += siz[c[now][0]] + 1, now = c[now][1];
		else now = c[now][0];
	}
	return rk;
}
int kth( int k ){
	int now = root;
	while( 1 ){
		if( siz[c[now][0]] == k - 1 ) return now;
		else if( siz[c[now][0]] >= k ) now = c[now][0];
		else k -= siz[c[now][0]] + 1, now = c[now][1];
	}
	return now;
}
int getn( int x ){
	int now = root;
	while( 1 ){
		if( val[now] == x ) return now;
		else now = c[now][x > val[now]];
	}
}
void delet( int k ){
	if( c[k][0] && c[k][1] ){
		int nd = c[k][0];
		while( c[nd][1] ) nd = c[nd][1];
		val[k] = val[nd];
		k = nd;
	}
	int son = c[k][0] ? c[k][0] : c[k][1], ws = c[far[k]][1] == k;
	far[c[far[k]][ws] = son] = far[k];
	for( int i = far[k]; i; i = far[i] ) siz[i]--;
	if( k == root ) root = son;
}
int succ( int x ){
	int now = root, ans = inf;
	while( now ){
		if( val[now] > x ) ans = min( ans, val[now] ), now = c[now][0];
		else now = c[now][1];
	}
	return ans;
}
int pred( int x ){
	int now = root, ans = -inf;
	while( now ){
		if( val[now] < x ) ans = max( ans, val[now] ), now = c[now][1];
		else now = c[now][0];
	}
	return ans;
}
int main(){
	init();
	scanf("%d", &n);
	for( int i = 1, opt, x; i <= n; i++ ){
		scanf( "%d%d", &opt, &x );
		if( opt == 1 ) insert( x );
		if( opt == 2 ) delet( getn(x) );
		if( opt == 3 ) printf("%d\n", rank(x));
		if( opt == 4 ) printf("%d\n", val[kth(x + 1)]);
		if( opt == 5 ) printf("%d\n", pred(x));
		if( opt == 6 ) printf("%d\n", succ(x));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值