【BZOJ3224】普通平衡树,SBT和failedSPT

0 篇文章 0 订阅

题意不赘述,不懂平衡树的自己书补,网补,脑补。脑补,蛤。

直接贴SBT代码,然后另带一份SplayTree的TLE代码。

SBT:

#include<cstdio>
#include<cstring>
#define N 2001000
using namespace std;
struct SBT
{
    int key,cnt;
    int l,r,size,sc;
}s[N];
int root,n,m;
void left_rotate(int &x)
{
    int y=s[x].r;
    s[x].r=s[y].l;
    s[y].l=x;
    s[y].size=s[x].size;
    s[y].sc=s[x].sc;
    s[x].size=s[s[x].l].size+s[s[x].r].size+1;
    s[x].sc=s[s[x].l].sc+s[s[x].r].sc+s[x].cnt;
    x=y;
}
void right_rotate(int &x)
{
    int y=s[x].l;
    s[x].l=s[y].r;
    s[y].r=x;
    s[y].size=s[x].size;
    s[y].sc=s[x].sc;
    s[x].size=s[s[x].l].size+s[s[x].r].size+1;
    s[x].sc=s[s[x].l].sc+s[s[x].r].sc+s[x].cnt;
    x=y;
}
void keep(int &x,int flag)
{
    s[x].size=s[s[x].l].size+s[s[x].r].size+1;
    s[x].sc=s[s[x].l].sc+s[s[x].r].sc+s[x].cnt;
    if(!flag)
    {
        if(s[s[s[x].l].l].size>s[s[x].r].size)right_rotate(x);
        else if(s[s[s[x].l].r].size>s[s[x].r].size)left_rotate(s[x].l),right_rotate(x);
        else return ;
    }
    else
    {
        if(s[s[s[x].r].r].size>s[s[x].l].size)left_rotate(x);
        else if(s[s[s[x].r].l].size>s[s[x].l].size)right_rotate(s[x].r),left_rotate(x);
        else return ;
    }
    keep(s[x].l,0);
    keep(s[x].r,1);
    keep(x,0);
    keep(x,1);
}
void insert(int &x,int key)
{
    if(!x)
    {
        x=++n;
        s[x].size=s[x].sc=s[x].cnt=1;
        s[x].key=key;
    }
    else
    {
        if(key==s[x].key)s[x].cnt++,s[x].sc++;
        else if(key<s[x].key)insert(s[x].l,key),keep(x,0);
        else insert(s[x].r,key),keep(x,1);
    }
     
}
void del(int &x,int key)
{
    if(s[x].key==key)
    {
        if(s[x].cnt>1)s[x].cnt--,s[x].sc--;
        else if(!s[x].l&&!s[x].r)x=0;
        else if(!s[x].l*s[x].r)x=s[x].l+s[x].r;
        else if(s[s[x].l].size>s[s[x].r].size)right_rotate(x),del(s[x].r,key),keep(x,0);
        else left_rotate(x),del(s[x].l,key),keep(x,1);
    }
    else if(key<s[x].key)del(s[x].l,key),keep(x,1);
    else del(s[x].r,key),keep(x,0);
}
int find(int &x,int k)
{
    if(s[s[x].l].sc<k&&k<=s[s[x].l].sc+s[x].cnt)return s[x].key;
    if(k<=s[s[x].l].sc)return find(s[x].l,k);
    else return find(s[x].r,k-s[s[x].l].sc-s[x].cnt);
}
int rank(int &x,int key)
{
    if(s[x].key==key)return s[s[x].l].sc+1;
    if(key<s[x].key)return rank(s[x].l,key);
    else return rank(s[x].r,key)+s[x].cnt+s[s[x].l].sc;
}
int getmin()
{
    int x=root;
    for(;s[x].l;x=s[x].l);
    return s[x].key;
}
int getmax()
{
    int x=root;
    for(;s[x].r;x=s[x].r);
    return s[x].key;
}
int pred(int &x,int y,int key)
{
    if(!x)return y;
    if(key<=s[x].key)return pred(s[x].l,y,key);
    else return pred(s[x].r,x,key);
}
int succ(int &x,int y,int key)
{
    if(!x)return y;
    if(key<s[x].key)return succ(s[x].l,x,key);
    else return succ(s[x].r,y,key);
}
int main()
{
    int i,t,x;
    scanf("%d",&m);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&t,&x);
        switch(t)
        {
            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",find(root,x));break;
            case 5:printf("%d\n",s[pred(root,0,x)].key);break;
            case 6:printf("%d\n",s[succ(root,0,x)].key);break;
        }
    }
    return 0;
}

SPT:(思想是对的。rotate和splay应该都是对的。)

#include <cstdio>
#include <algorithm>
#define N 201000
#define inf 0x3f3f3f3f
#define rt son[root][1]
#define lrt son[rt][0]
#define ls son[x][0]
#define rs son[x][1]
#define is(x) (x==son[fa[x]][1])
using namespace std;

struct SPT
{
	int root,top;
	int val[N],son[N][2],size[N],num[N],fa[N];
	inline void link(int &x,int y,int d){son[y][d]=x;fa[x]=y;}
	inline void pushup(int x)
	{
		size[x]=size[ls]+size[rs]+num[x];
	}
	inline void init()
	{
		top=0;
		newnode(root,0,-inf);
		newnode(son[root][1],root,inf);
		size[1]=size[2]=num[1]=num[2]=0;
	}
	inline void rotate(int x)
	{
		int y=fa[x],z=fa[y],idx=is(x),idy=is(y);
		link(son[x][!idx],y,idx);
		link(y,x,!idx);
		if(z)link(x,z,idy);fa[x]=z;
		pushup(y);
		pushup(x);
	}
	inline void splay(int x,int k=0)
	{
		int y,z;
		while(fa[x]!=k)
		{
			y=fa[x],z=fa[y];
			if(z==k){rotate(x);break;}
			if(is(x)==is(y))rotate(y);
			else rotate(x);
			rotate(x);
		}
		if(!k)root=x;
	}
	inline int pred(int w,int k=0)
	{
		int x=root,y;
		while(x)
		{
			if(w>val[x])y=x;
			x=son[x][w>val[x]];
		}
		splay(y,k);
		return val[y];
	}
	inline int succ(int w,int k=0)
	{
		int x=root,y;
		while(x)
		{
			if(w<val[x])y=x;
			x=son[x][w>=val[x]];
		}
		splay(y,k);
		return val[y];
	}
	inline void newnode(int &x,int y,int w)
	{
		x=++top;
		val[x]=w;
		fa[x]=y;
		size[x]=num[x]=1;
	}
	inline void insert(int w,int k=0)
	{
		int x=root;
		while(son[x][w>val[x]])
		{
			if(w==val[x])
			{
				num[x]++;
				splay(x,k);
				return ;
			}
			x=son[x][w>val[x]];
		}
		newnode(son[x][w>val[x]],x,w);
		splay(son[x][w>val[x]]);
	}
	inline bool remove(int w)
	{
		pred(w);succ(w,root);
		if(!lrt)return 0;
		if(num[lrt]-1)num[lrt]--;
		else son[rt][0]=0;
		pushup(rt);
		pushup(root);
		return 1;
	}
	inline int rank(int w,int k=0)
	{
		int x=root;
		while(son[x][w>val[x]])
		{
			if(w==val[x])break;
			x=son[x][w>val[x]];
		}
		splay(x,k);
		return size[son[x][0]]+1;
	}
	inline int find(int rank,int k=0)
	{
		int x=root;
		while(rank<=size[son[x][0]]||size[son[x][0]]+num[x]<rank)
		{
			if(rank<=size[son[x][0]])x=son[x][0];
			else x=son[x][1],rank-=(size[son[x][0]]+num[x]);
		}
		splay(x,k);
		return val[x];
	}
	inline void dfs(int x)
	{
		if(son[x][0])dfs(son[x][0]);
		if(val[x]>0&&val[x]<inf)for(int i=1;i<=num[x];i++)printf("%d\n",val[x]);
		if(son[x][1])dfs(son[x][1]);
	}
}spt;

int main()
{
//	freopen("test.in","r",stdin);
	int i,t,k,n;
	scanf("%d",&n);
	spt.init();
	for(i=1;i<=n;i++)
	{
		scanf("%d",&t);
		spt.insert(t);
	}
	spt.dfs(spt.root);
	return 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、付费专栏及课程。

余额充值