假spaly害人-洛谷P1486 郁闷的出纳员

https://www.luogu.org/problem/show?pid=1486#sub
我以前的spaly他妈全抄模版的,然后觉得这样太颓废了,就很装逼地想自己写这题;
其实我理论都懂的,所以我就认为自己应该何以靠自己的力量去做出来;
然后做了两个晚上+1H的在校时间;
算算有6,7个小时呢;
真日了狗了
网上的模版反正烂大街的,这道题hzwer的spaly写的很烂的,结果过了(bz过了,洛谷死循环)
然后你随便翻开一个模版就看到什么zigzag压成一个函数啊,什么函数参数带&这个东西;
这样并不会给程序加速;
我也不觉得这样会方便;
我反正就是朴实的spaly;


struct SP{
    int l,r,v,s,w,fa;//l,r区间,v值,s这个节点为跟的树有几个节点,w是权值相同 的数量 
}sp[100005];
int n,m,x,y,z,sum,ans,nn,Min,rt,num;//sum是全局变化的lazy标记 

这题的思路我不想讲了,我做了6,7个小时不是卡在思路上的;
关于思路你们就自己去找别的题解或者看代码把;
但是我要讲一下关于del的问题;
我们有一个x(代码中x指Min-sum),现在我们要把所有权值小于x的节点全部删掉;
std:我们找到x的后继,把他提到根节点,然后把左子树删光;很有道理对不对;
我们看一下正确的代码;

int del(int te){//返回删了几个 
    int k=0,num=-1,x=rt;
    while(x)
        if(sp[x].v<te)x=sp[x].r;else{
            if(sp[x].v<k||!k){
                k=sp[x].v; num=x;
            }
            x=sp[x].l;
        }
    if(num==-1){
        k=sp[rt].s; rt=0;return k;
    }
    splay(num,sp[rt].fa);
    k=sp[sp[rt].l].s;
    sp[rt].s-=k;
    sp[rt].l=0;
    return k;
}

没什么毛病;
但是这是我一开始写的代码

int del(int te){
    int k=0,num=rt,x=rt;
    while(x)
        if(sp[x].v<te)x=sp[x].r;else{
            if(sp[x].v<k||!k){
                k=sp[x].v; num=x;
            }
            x=sp[x].l;
        }
    if(!num){
        k=sp[rt].s; rt=0;return k;
    }
    splay(num,sp[rt].fa);
    k=sp[sp[rt].l].s;
    sp[rt].s-=k;
    sp[rt].l=0;
    return k;
}

这个就WA了,因为我没有考虑关于全部删除的问题,所以num一开始要先赋值-1,最后特判;
这种细节是抄板子的人所不会考虑的;
然后我们再来看看我之前写的错误的del;
为什么错误,不明真相;

void del(int x){
    while(sp[x].l*sp[x].r){
        zag(x);
        sp[sp[x].fa].s-=sp[x].w;
    }
    if(x==rt)rt=sp[x].l+sp[x].r;else
    if(sp[sp[x].fa].v<sp[x].v)sp[sp[x].fa].r=sp[x].l+sp[x].r;else sp[sp[x].fa].l=sp[x].l+sp[x].r;
    sp[sp[x].l+sp[x].r].fa=sp[x].fa;
}
void dfs(int k){
    if(!k)return;
    if(sp[k].v+sum<Min){ans+=sp[k].w;q[++tot]=k;dfs(sp[k].r);}
    dfs(sp[k].l);
}
if(cc=='S'){
            sum-=x;
            dfs(rt);
            while(tot)del(q[tot--]);
        }

这个是我一开始的写法,就是把不符合 的数一个一个找出来一个一个删掉;死循环;
我也不知道这个怎么错了;
估计就是父节点说明搞错,少了说明特判;
然后是改进版;

int dfs(int k){
    if(!k)return 0;
    int ans=0;
    if(sp[k].v+sum<Min){
        ans=sp[sp[k].l].s+sp[k].w;
        if(k==rt)rt=sp[k].r;else
        if(sp[sp[k].fa].v<sp[k].v)sp[sp[k].fa].r=sp[k].r;
        else sp[sp[k].fa].l=sp[k].r;
        if(sp[k].r)sp[sp[k].r].fa=sp[k].fa;
        ans+=dfs(sp[k].r);
    }else ans=dfs(sp[k].l);
    sp[k].s-=ans;
    return ans;
}

这个就是找到不对的点,直接把左子树全部删掉,把右子树接起来;
又踏马不知道哪里错了;
估计又是什么地方没特判;
那个人说splay好写的,你给我站出来!!
然后如果您看出上面的俩个程序的问题所在,告诉我谢谢;


总结
1.链接节点的时候一定要考虑全面,而且要特判跟的情况;
2.各种情况都要特判;
3.不要直接抄别人的模版;

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
struct SP{
    int l,r,v,s,w,fa;
}sp[100005];
int tot,q[100005];
int n,m,x,y,z,sum,ans,nn,Min,rt,num;
char cc;
void up(int x){sp[x].s=sp[sp[x].l].s+sp[sp[x].r].s+sp[x].w;}
void zig(int x){
    int y=sp[x].r;
    if(x==rt)rt=y;else
    if(sp[sp[x].fa].v<sp[x].v)sp[sp[x].fa].r=y;else sp[sp[x].fa].l=y;
    sp[sp[y].l].fa=x;
    sp[x].r=sp[y].l;
    sp[y].l=x;
    sp[y].fa=sp[x].fa; 
    sp[x].fa=y;
    sp[y].s=sp[x].s;
    up(x);
}
void zag(int x){
    int y=sp[x].l;
    if(x==rt)rt=y;else
    if(sp[sp[x].fa].v<sp[x].v)sp[sp[x].fa].r=y;else sp[sp[x].fa].l=y;
    sp[sp[y].r].fa=x;
    sp[x].l=sp[y].r;
    sp[y].r=x;
    sp[y].fa=sp[x].fa;
    sp[x].fa=y;
    sp[y].s=sp[x].s;
    up(x);
}
void splay(int x,int k){
    while(sp[x].fa!=k){
        int y=sp[x].fa,z=sp[y].fa;
        if(z!=k)
            if((sp[y].v<sp[x].v)^(sp[z].v<sp[y].v))
                if(sp[y].v<sp[x].v)zig(y);else zag(y);
                else
                if(sp[z].v<sp[y].v)zig(z);else zag(z);
        y=sp[x].fa; z=sp[y].fa;
        if(sp[y].v<sp[x].v)zig(y);else zag(y);
    }
}
void insert(int x){
    if(!rt){rt=++nn;sp[rt].s=sp[rt].w=1;sp[rt].v=x;return;}
    int k=rt,kk;
    while(k){
        kk=k;
        sp[k].s++;
        if(sp[k].v==x){sp[k].w++;return;}
        if(sp[k].v<x)k=sp[k].r;else k=sp[k].l;
    }
    sp[++nn].v=x; sp[nn].fa=kk; sp[nn].s=sp[nn].w=1; 
    if(sp[kk].v<x)sp[kk].r=nn;else sp[kk].l=nn;
    splay(nn,sp[rt].fa);
}
int del(int te){
    int k=0,num=-1,x=rt;
    while(x)
        if(sp[x].v<te)x=sp[x].r;else{
            if(sp[x].v<k||!k){
                k=sp[x].v; num=x;
            }
            x=sp[x].l;
        }
    if(num==-1){
        k=sp[rt].s; rt=0;return k;
    }
    splay(num,sp[rt].fa);
    k=sp[sp[rt].l].s;
    sp[rt].s-=k;
    sp[rt].l=0;
    return k;
}
int find(int k,int x){
    if(x>num-ans)return -1-sum;
    if(sp[sp[k].r].s+1<=x&&x<=sp[sp[k].r].s+sp[k].w)return sp[k].v;
    if(sp[sp[k].r].s>=x)return find(sp[k].r,x);
    return find(sp[k].l,x-sp[sp[k].r].s-sp[k].w);
}
int main()
{ 
    scanf("%d%d",&m,&Min);
    while(m--){
        scanf("\n%c %d",&cc,&x);
        if(cc=='I'&&x<Min)continue;else
        if(cc=='I')insert(x-sum),num++;else
        if(cc=='A')sum+=x;else
        if(cc=='S'){
            sum-=x;
            ans+=del(Min-sum);
        }else printf("%d\n",find(rt,x)+sum);
    }
    printf("%d",ans);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值