BZOJ1503 NOI2004 郁闷的出纳员 题解&代码

26 篇文章 0 订阅
6 篇文章 0 订阅

题意太傻不多解释= =就是维护一个档案队列,按节点val建树

思路:
从query操作(命令F)可以看出,这棵树的顺序核心在于value而不是一般的维护队列,这样的话相同value的节点显而易见地应该放在一起,我们除了s[]记录子树大小之外,额外增加一个z[]记录节点大小(对于x节点来说每有一个与其value重复的z[x]++),注意z[]不需要维护。
然后就是喜闻乐见的标记了,这道题算是比较良心= =只需要维护两个标记
add[]对当前子树进行范围修改的lazy标记
val[]单点val,需要在维护add[]的时候一起维护

然后= =就是想起来就有种自己傻的反思时间
1、Splay居然又写错了!循环时没有同步更新y和z!调了半个小时才输出出来错在这!
2、最初思路不够清晰就开始写,结果用值查找节点的时候因为想要在多个函数中用到getval这个函数两面都想满足= =结果两边都求错了【23333
3、最开始没有想到节点值相同时该怎么维护,结果只好新建节点维护成一个队列= =结局可想而知
4、查找第k大的时候想错= =如果当前节点val大于目标val直接向左走了,其实应该记录一下路径中最接近目标val的节点
5、query的时候顺序反了= =结果自己怎么看都没错,输出就是有问题…在更新了x之后想要得到关于原x的信息是自己活在梦里

于是…用了一个半小时写完了代码用了四个半小时调= =下一道Splay再这样就药丸了

#include<iostream>
#include<stdio.h>
#define lson ch[x][0]
#define rson ch[x][1]
using namespace std;
const int maxn=100005;
int n,m,k,ans,tot,root;
int ch[maxn][2],fa[maxn],add[maxn],val[maxn],s[maxn],z[maxn];
char str[5];
void link(int x,int y,int d)
{
    if(y)ch[y][d]=x;
    if(x)fa[x]=y;
    //cout<<"link "<<x<<' '<<fa[x]<<' '<<d<<endl;
}
int ischild(int x)
{
    return ch[fa[x]][1]==x;
}
void maintain(int x)
{
    if(x)s[x]=s[lson]+s[rson]+z[x];
}
void pushdown(int x)
{
    if(!add[x])return;
    if(lson)add[lson]+=add[x],val[lson]+=add[x];
    if(rson)add[rson]+=add[x],val[rson]+=add[x];
    add[x]=0;
}
void rotate(int x)
{
    int d=ischild(x);
    int y=fa[x],z=fa[y];
    if(y==root)root=x;
    link(x,z,ischild(y));
    link(ch[x][!d],y,d);
    link(y,x,!d);
    maintain(y);
}
void Splay(int x,int goal=0)
{
    int y=fa[x],z=fa[y];
    while(y!=goal)
    {
        pushdown(z);
        pushdown(y);
        pushdown(x);
        if(z==goal)
        {
            rotate(x);
            break;
        }
        if(ischild(x) ^ ischild(y))rotate(x);
        else rotate(y);
        rotate(x);
        y=fa[x],z=fa[y];//false
    }
    maintain(x);
}
int getnext(int x)
{
    x=ch[x][1];
    while(ch[x][0])x=ch[x][0];
    return x;
}
int getval(int k)
{
    int x=root;
    while(x)
    {
        pushdown(x);
        if(val[x]==k)return x;//false
        if(ch[x][val[x]<k])x=ch[x][val[x]<k];
        else return x;
    }
    return x;
}
int newnode(int v)
{
    int t=++tot;
    s[t]=z[t]=1;
    val[t]=v;
    return t;
}
void addtree(int k)
{
    if(!root)
    {
        root=newnode(k);
        return;
    }
    int x=getval(k);
    if(val[x]==k)z[x]++;
    else link(newnode(k),x,val[x]<k);
    Splay(x);
}
void increase(int k)
{
    if(!root)return;
    add[root]+=k;
    val[root]+=k;
}
void decrease(int k)
{
    if(!root)return;
    add[root]-=k;
    val[root]-=k;
    int x=root,pre=0;
    while(x)
    {
        pushdown(x);
        if(val[x]==m)break;//false
        if(val[x]<m)x=ch[x][1];
        else
        {
            pre=x;
            x=ch[x][0];
        }
    }
    if(!x)x=pre;
    if(x)Splay(x);
    if(!x)ans+=s[root],root=0;
    else ans+=s[ch[x][0]],ch[x][0]=0;
    maintain(x);
}
int query(int k)
{
    int x=root;
    int t;
    while(x)
    {
        pushdown(x);
        t=s[ch[x][1]];
        if(t>=k)x=ch[x][1];
        else
            if(t+z[x]>=k)return val[x];
            else k-=t+z[x],x=ch[x][0];//else x=ch[x][0],k-=t+z[x];false
    }
    if(x)return val[x];
    else return -1;
}
int que(int x,int k)
{
    if(!x)return -1;
    pushdown(x);
    if(s[ch[x][1]]>=k)return que(ch[x][1],k);
    if(s[ch[x][1]]+z[x]>=k)return val[x];
    return que(ch[x][0],k-s[ch[x][1]]-z[x]);
}
int main(void)
{
    //freopen("cashier2.in","r",stdin);
    //freopen("cashier.ans","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s%d",str,&k);
        //printf("%c %d\n",str[0],k);
        if(str[0]=='I')
            if(k>=m)addtree(k);
        if(str[0]=='A')increase(k);
        if(str[0]=='S')decrease(k);
        if(str[0]=='F')printf("%d\n",query(k));
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值