低级数据结构

eg.1   uva_11983 

核心有三点:横坐标扫描线+纵坐标离散化+自定义线段树懒操作 

首先自定义线段树以及其懒操作,方法非常值得借鉴,题目提示了k<=10,当时也注意到了,但是还是停留在最初级的线段树思维,并没有充分利用。

回忆一下:

struct node
{
    int l,r,add;
    int mid()
    {
        return (l+r)/2;
    }
    ll a[11];
} t[24*maxn];

1,add无疑是lag操作。2,a[i]=j,表示当前l到r内数值为i的有j个。

更重要的 整个线段树中并没有push_down函数,却有push_up函数,并且并不像区间求和那样一句话结束。

回忆一下:

void push_up(int id)
{
    memset(t[id].a,0,sizeof(t[id].a));
    if(t[id].l==t[id].r)
    {
        int x=t[id].l;
        t[id].a[min(k,t[id].add)]=np[x]-np[x-1];
    }
    else
    {
        for(int i=0; i<=k; i++)
        {
            int cmp=min(k,i+t[id].add);
            t[id].a[cmp]+=t[LL(id)].a[i]+t[RR(id)].a[i];
        }
    }
}

else后好理解,else前是统计离散化前的节点个数(np)。非常值得借鉴。

接下来是纵坐标离散化,与其说是离散化,不如说是将线段分割成点。一般这种情况可以将每个点分割成三个。


其实更好的是:从第一个点到最后倒数第二个点建树,后面的点和当前的点做差,这样五条线段就会变成

(l-1,l-1)(l,l)(l+1,r-2)  (r-1,r-1)(r,r)其实和上面是一样的= =也可以写成上面那种形式

但是上面的话第一个是没用的,如果是下面的话就是最后一个是没用的(没有写出来(r+1,+无穷))。下面的话只需要m--就可以了。


最后是扫描线。

 for(int i=0;i<2*n-1;i++)
    {
        update(c[i].l,c[i].r,c[i].v,1);
        ans+=t[1].a[k]*(c[i+1].pos-c[i].pos);
    }
每次更新完之后看后一条线段的位置,而不能看前一条线段的位置。如果看前面的位置的话就会比较麻烦。(自行想象)。相反,如果看后面的位置的话就会发现很简单,不用过多的修饰。(get)


eg2. hdu_3436

好像有splay(伸展树)的写法,但是对于我这种蒟蒻还不知道伸展树的情况,只会用树状数组慢慢搞出来。QAQ

核心:离散化+(树状数组+二分)+预存。

“预存”:每次将某个元素置顶的时候,我们都需要将其抽出来,放到数组的最前面,那么如果我们如果提前知道了置顶的操作数,我们可以初始的时候将所有数往后挪“操作数”个。同时用树状数组来维护前k个位置有多少个数。

int pos[maxn];映射后第i个人在树状数组上的位置j
int who[2*maxn];第i个位置是第j人(无映射)

单独讲几个操作:

置顶:由于离散化的关系,我们可以知道那个人在数组中的位置,然后把他前面的几个人的位置都+1

update(pos[x],-1);
who[pos[x]]--;
who[k]=p[x];
pos[x]=k;
update(k,1);
k--;
这里who要减1 并不会出现问题,因为树状数组的关系,当他非法的时候树状数组的值是0,巧妙。


询问排名是谁:

树状数组套二分,找到第一个前缀和不小于该排名的节点。那么所找的排名必定前一个节点的前缀和到这个节点的前缀和,那么知道该节点当前的排名只需要再做一些处理就可以了。


询问谁是排名:

由于都离散化过只需要知道该节点在树状数组的位置,树状数组询问一遍该位置的前缀和就行了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值