码迷,mamicode.com
首页 > 其他好文 > 详细

伸展树复习

时间:2017-03-28 21:32:06      阅读:297      评论:0      收藏:0      [点我收藏+]

标签:app   节点   update   中序   one   数组   tar   编号   open   

T1 郁闷的出纳员

一个数据结构,支持单点插入、删除几个不一定连续的点、查询k值操作

初做:2017.2.18   time:1268ms    memory:3MB

http://www.cnblogs.com/TheRoadToTheGold/p/6412790.html

现在:2017.3.28   time:570ms   memory:3MB

初做时直接套模板,删除分5种情况,

本题实际只需3种情况

这一次的查询用递归写的

int query(int now,int k)
{
	int tmp=0;
	if(ch[now][0]) tmp=sum[ch[now][0]];
	if(k<=tmp) return query(ch[now][0],k);
	if(tmp+cnt[now]>=k) return key[now];
	return query(ch[now][1],k-tmp-cnt[now]);
}

5行开始写的时候每一行都有错误

第二行:tmp=左子树节点个数,遗漏了判断是否有左子树

第三行:当k<=左子树节点个数,漏了等于号

第四行:左子树节点个数+根节点个数(cnt[]数组)>=k,cnt[]和sum[]混了,>写的<

第五行:cnt和sum混了

 

再就是splay写丑了

贴个精简的:

inline void splay(int x)
{
    for(int fa;fa=f[x];rotate(x))
      if(f[fa]) rotate(getson(x)==getson(fa) ? fa:x);
    root=x;
    update(x);
}

 

一定要注意update时节点是否有左右孩子

 

rotate里出现了一个错误:

if(z) ch[z][ch[z][1]==y]=x;

fa[x]=z;这一句是不在if(z)里的

技术分享
#include<cstdio>
#define N 100001
using namespace std;
int n,limit,gather,root,tot,leave;
int sum[N],ch[N][2],fa[N],key[N],cnt[N];
void newroot(int x)
{
    sum[++tot]=cnt[tot]=1;
    key[tot]=x;
    root=tot;
}
void newnode(int f,int x)
{
    sum[++tot]=cnt[tot]=1;
    key[tot]=x;
    fa[tot]=f;
    ch[f][x>key[f]]=tot;
}
void update(int x)
{
    sum[x]=cnt[x];
    if(ch[x][0]) sum[x]+=sum[ch[x][0]];
    if(ch[x][1]) sum[x]+=sum[ch[x][1]];
}
void rotate(int x,int kind)
{
    int y=fa[x],z=fa[y];
    ch[y][!kind]=ch[x][kind]; fa[ch[y][!kind]]=y;
    ch[x][kind]=y;fa[y]=x;
    fa[x]=z;
    if(z) ch[z][ch[z][1]==y]=x;
    update(y);
}
void splay(int x)
{
    while(fa[x])
    {
        int y=fa[x],kind=ch[y][1]==x;
        if(!fa[y]) rotate(x,!kind);
        else
        {
            int z=ch[fa[y]][1]==y;
            if(z==kind) 
            {
                rotate(y,!kind);
                rotate(x,!kind);
            }
            else
            {
                rotate(x,!kind);
                rotate(x,kind);
            }
        }
    }
    update(x);
    root=x;
}
void insert(int x)
{
    if(!root) 
    {
        newroot(x);
        return;
    }
    int now=root;
    while(ch[now][x>key[now]])
    {
        if(key[now]==x)
        {
            cnt[now]++;
            update(now);
            splay(now);
            return;
        }
        now=ch[now][x>key[now]];
    }
    if(key[now]==x)
    {
        cnt[now]++;
        update(now);
        splay(now);
        return;
    }
    newnode(now,x);
    update(now);
    splay(tot);
}
void cut_lch()
{
    int tmp=ch[root][0];
    if(tmp) leave+=sum[tmp];
    fa[tmp]=0; ch[root][0]=0;
    update(root);
}
void cut()
{
    if(cnt[root]>1)
    {
        cnt[root]--;
        update(root);
        return;
    }
    if(!ch[root][1])
    {
        root=0;
        gather=0;
        return;
    }
    int tmp=ch[root][1];
    fa[tmp]=0;
    ch[root][1]=0;
    root=tmp;
}
int query(int now,int k)
{
    int tmp=0;
    if(ch[now][0]) tmp=sum[ch[now][0]];
    if(k<=tmp) return query(ch[now][0],k);
    if(tmp+cnt[now]>=k) return key[now];
    return query(ch[now][1],k-tmp-cnt[now]);
}
int main()
{
    freopen("cashier.in","r",stdin);
    freopen("cashier.out","w",stdout);
    scanf("%d%d",&n,&limit);
    char ch[1];int x;
    while(n--)
    {
        scanf("%s%d",ch,&x);
        switch(ch[0])
        {
            case I:
                if(x<limit) continue;
                insert(x-gather); break;
            case A:
                gather+=x; break;
            case S:
                gather-=x;
                insert(limit-gather);
                cut_lch();
                cut();
                break;
            case F:
                if(x>sum[root]) printf("-1\n");
                else printf("%d\n",query(root,sum[root]-x+1)+gather);
        }
    }
    printf("%d",leave);
}
View Code

 

 

T2 反转卡片

http://www.cnblogs.com/TheRoadToTheGold/p/6414979.html

初做:2017.2.19  现在:2017.3.28

3个小时

这个题使splay中的节点编号与节点在序列中的顺序保持一致,然后splay的中序遍历就是卡片顺序。

所以第i张卡片相当于splay中排名为i的节点,而且不需要在splay中存储权值,即卡片上的数字

所以以前认为将卡片编号作为权值建splay是错误的

 

技术分享
#include<cstdio>
#include<algorithm>
#define N 300010
using namespace std;
int n,num[N],root;
int fa[N],ch[N][2],sum[N];
bool tag[N];
void update(int x)
{
    sum[x]=1+sum[ch[x][0]]+sum[ch[x][1]];
}
void build(int l,int r,int f)
{
    if(l>r) return;
    int mid=l+r>>1;
    fa[mid]=f;sum[mid]=1;
    ch[f][mid>f]=mid;
    build(l,mid-1,mid);
    build(mid+1,r,mid);
    update(mid);
}
int getson(int x)
{
    return ch[fa[x]][1]==x;
}
void rotate(int x,int & goal)
{
    int y=fa[x],z=fa[y],kind=getson(x);
    if(y==goal) goal=x;
    else ch[z][ch[z][1]==y]=x;
    ch[y][kind]=ch[x][!kind]; fa[ch[y][kind]]=y;
    ch[x][!kind]=y; fa[y]=x;
    fa[x]=z;
    update(y);
}
void down(int x)
{
    tag[x]^=1;
    if(ch[x][0]) tag[ch[x][0]]^=1;
    if(ch[x][1]) tag[ch[x][1]]^=1;
    swap(ch[x][0],ch[x][1]);
}
void splay(int x,int & goal)
{
    while(x!=goal)
    {
        if(tag[fa[fa[x]]]) down(fa[fa[x]]);
         if(tag[fa[x]]) down(fa[x]);
          if(tag[x]) down(x);
        int y=fa[x];
        if(y!=goal)
        {
            if(getson(y)==getson(x)) rotate(y,goal);
            else rotate(x,goal);
        }
        rotate(x,goal);    
    }
    update(x);
}
int find(int now,int x)
{
    if(tag[now]) down(now);
    int tmp=0;
    if(ch[now][0]) tmp=sum[ch[now][0]];
    if(x<=tmp) return find(ch[now][0],x);
    if(tmp+1>=x) return now;
    return find(ch[now][1],x-tmp-1);
}
void rever(int l,int r)
{
    int x=find(root,l-1),y=find(root,r+1);
    splay(x,root);splay(y,ch[x][1]);
    tag[ch[y][0]]^=1;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&num[i+1]);
    build(1,n+2,0);
    root=(n+3)/2;
    int maxn=100000,id,r,ans=0;
    while(maxn--)
    {
        id=find(root,2);
        r=num[id];
        if(r==1)
        {
            printf("%d",100000-maxn-1);
            return 0;
        }
        rever(2,r+1);
    }
    printf("-1");
}
View Code

 

列举代码中好几个错误:

①、答案的输出

if(r==1)
{
    printf("%d",100000-maxn-1);
    return 0;
}

而不是  100000-maxn

因为开头写的 while(maxn--)

判断完maxn为true后,减1,输出里的maxn相对于while里已经减了1,所以应该是100000-(maxn+1)

②、rever函数中,打翻转标记应该是tag[ch[y][0]]^=1;

而不是tag[ch[y][0]]=1

③、find函数里now和x混了,属于不过脑子的错误

④、splay里用了这个

inline void splay(int x)
{
    for(int fa;fa=f[x];rotate(x))
      if(f[fa]) rotate(getson(x)==getson(fa) ? fa:x);
    root=x;
    update(x);
}

上面的判断标准是f[fa]为true,因为根节点的父节点是0

这个题是将点转到指定位置,所以判断标准是父节点是否是目标位置

修改上面的代码改了好久就是不过

所以,若是将点转到指定位置,干脆用这个

void splay(int x,int & goal)
{
    while(x!=goal)
    {
        if(tag[fa[fa[x]]]) down(fa[fa[x]]);
         if(tag[fa[x]]) down(fa[x]);
          if(tag[x]) down(x);
        int y=fa[x];
        if(y!=goal)
        {
            if(getson(y)==getson(x)) rotate(y,goal);
            else rotate(x,goal);
        }
        rotate(x,goal);    
    }
    update(x);
}

原来写的代码没有下传标记A了,很神奇

⑤、rotate 里

    if(y==goal) goal=x;
    else ch[z][ch[z][1]==y]=x;

而不是

if(z)ch[z][ch[z][1]==y]=x;

⑥、build里 update(mid)而不是update(f)

 

还有一点:这里所有有关父节点与子节点的操作,都不需要判断是否有子节点

why?

 

伸展树复习

标签:app   节点   update   中序   one   数组   tar   编号   open   

原文地址:http://www.cnblogs.com/TheRoadToTheGold/p/6635514.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!