【HDU 6973】Bookshop 树剖+平衡树

【HDU 6973】Bookshop 树剖+平衡树

【引言】

​ 平衡树的题做得比较少,难得补一次神题,记录一下供以后学习。

【题意】

​ 给出一棵 n n n 个结点的树,每个结点有一个价值为 p i p_i pi 的商品。

​ 有 m m m 次询问,每次问如果一个人带着 w w w 块钱,从 x x x 结点出发到达 y y y 结点结束,在路上遇到能买的东西就买,不能买就继续往前走,等他走完这段路之后,还剩多少钱?(询问之间相互独立。)

​ 数据范围: n , m ≤ 1 0 5 n,m\le 10^5 n,m105 p i , w ≤ 1 0 9 p_i,w\le 10^9 pi,w109

【分析】

​ 容易想到树剖把一个询问的问题分段。

​ 假设可以把 x → y x\to y xy 分成 x → z , z n e x t → y x\to z,z_{next}\to y xz,znexty 这连续的两段,那么钱的剩余值 r e m a i n ( x , y , w ) = r e m a i n ( z n e x t , y , r e m a i n ( x , z , w ) ) remain(x,y,w) =remain(z_{next},y,remain(x,z,w)) remain(x,y,w)=remain(znext,y,remain(x,z,w))

​ 也就是说分段之后,只要能按顺序解决每一段的问题,就能解决整个询问的问题。

​ 那么树剖就可以把一个询问分成 O ( log ⁡ n ) O(\log n) O(logn) 段,我们还可以让重链上的 d f s dfs dfs 序连续。

​ 对每个询问,一定都是先从 x x x 走若干逆 d f s dfs dfs 序的连续段,然后到达 L C A LCA LCA ,再从 L C A LCA LCA 走若干顺 d f s dfs dfs 序的连续段到达 y y y

​ 于是,我们把每个询问剖出来的每个连续段分为正逆两类。

​ 然后,一起做所有询问的逆 d f s dfs dfs 序部分,也就是说按照逆 d f s dfs dfs 序扫描每个商品,遇到了一个询问的起点,就把这个询问对应的起始金钱丢入一个数据结构中,过了一个询问的中点,就把这个询问对应的剩余金钱从数据结构中取出来。然后每扫过一个 p i p_i pi ,数据结构都要对内部 [ p i , + ∞ ) [p_i,+\infin) [pi,+) 的键值进行减 p i p_i pi

​ 可以用平衡树做这个数据结构。

​ 区间减的时候分为两部分,对于 [ p i , 2 p i ) [p_i,2p_i) [pi,2pi) 的键值,暴力取出来然后改值之后塞进去,均摊每个值最多操作 O ( log ⁡ w ) O(\log w) O(logw) 次。

​ 对于 [ 2 p i , + ∞ ) [2p_i,+\infin) [2pi,+) 直接打 tag 维护,因为减掉之后互不影响相对位置。

​ 逆 d f s dfs dfs 序部分做完之后,类似的做一遍顺 d f s dfs dfs 序就行了。

​ 总复杂度可以这么计算,每个询问变成了 O ( log ⁡ n ) O(\log n) O(logn) 段,但是题目性质保证我们在扫描的时候平衡树的 s i z e size size O ( n ) O(n) O(n) ,平衡树的操作还是 O ( log ⁡ n ) O(\log n) O(logn) 的,再算上每个询问有 O ( log ⁡ w ) O(\log w) O(logw) 次平衡树上的暴力修改,总复杂度大致是 O ( n log ⁡ 2 n + n log ⁡ n log ⁡ w ) O(n\log^2 n +n\log n\log w ) O(nlog2n+nlognlogw)

​ 最后我用的 FHQ Treap \text{FHQ Treap} FHQ Treap 实现的,代码长度有点窒息。

​ 要注意一下 FHQ Treap \text{FHQ Treap} FHQ Treap 的插入,需要硬拆开再合并。

#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 100011
#define LL long long
using namespace std;
int n,m;
int tot,pre[MAXN],lin[MAXN*2],to[MAXN*2];
int fa[MAXN],son[MAXN],dep[MAXN],siz[MAXN],dfn[MAXN],anti_dfn[MAXN];
int num;
int top[MAXN];
int p[MAXN];
struct Query{
    int st,ed;
    int w,id,tree_node;
}q[MAXN];
struct WALK{
    bool is_st;
    int loc;
    int belong;
    WALK(){}
    WALK(int _is_st,int _loc,int _belong):is_st(_is_st),loc(_loc),belong(_belong){}
};
bool cmp(WALK x,WALK y)
{
    return x.loc==y.loc?x.is_st > y.is_st : x.loc < y.loc;
}
bool rev_cmp(WALK x,WALK y)
{
    return x.loc==y.loc?x.is_st > y.is_st : x.loc > y.loc;
}
vector<WALK > vec,rev_vec;

void add(int x,int y)
{
    tot++;lin[tot]=pre[x];pre[x]=tot;to[tot]=y;
}
void dfs1(int x)
{
    siz[x]=1;
    for(int i=pre[x];i;i=lin[i])
    {
        int v=to[i];
        if(v==fa[x])continue;
        fa[v]=x;
        dep[v]=dep[x]+1;
        dfs1(v);
        siz[x]+=siz[v];
        if(siz[son[x]]<siz[v])
            son[x]=v;
    }
}
void dfs2(int x)
{
    dfn[x]=++num;
    anti_dfn[num]=x;
    if(x==son[fa[x]])
        top[x]=top[fa[x]];
    else
        top[x]=x;
    if(son[x])
        dfs2(son[x]);
    for(int i=pre[x];i;i=lin[i])
    {
        int v=to[i];
        if(v==fa[x]||v==son[x])continue;
        dfs2(v);
    }
}
void LCA(int x,int y,int belong)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]] > dep[top[y]])
        {
            rev_vec.push_back(WALK(true,dfn[x],belong));
            rev_vec.push_back(WALK(false,dfn[top[x]],belong));
            x=fa[top[x]];
        }
        else
        {
            vec.push_back(WALK(true,dfn[top[y]],belong));
            vec.push_back(WALK(false,dfn[y],belong));
            y=fa[top[y]];
        }
    }
    if(dep[x] > dep[y])
    {
        rev_vec.push_back(WALK(true,dfn[x],belong));
        rev_vec.push_back(WALK(false,dfn[y],belong));
    }
    else
    {
        vec.push_back(WALK(true,dfn[x],belong));
        vec.push_back(WALK(false,dfn[y],belong));
    }   
}
struct QUEUE{
    int q[MAXN],head,tail;
    QUEUE(){head=tail=0;}
    bool empty(){return tail==head;}
    void pop(){head++;if(head==MAXN)head=0;}
    void push(int x){q[tail++]=x;if(tail==MAXN)tail=0;}
    int front(){return q[head];}
    void clear(){head=tail=0;}
};
struct FHQ_Treap{
    static const int Null = 0;
    QUEUE que;
    int num,root;
    struct node{
        int l,r,fa;
        int rnd,val,belong,siz,tag;
        node(){siz=tag=0;}
    }tr[MAXN*2];

    void init(){num=0;root=Null;que.clear();}
    void pushup(int x)
    {
        tr[x].siz=tr[tr[x].l].siz+tr[tr[x].r].siz+1;
        tr[tr[x].l].fa=tr[tr[x].r].fa=x;
    }
    void pushdown(int x)
    {
        if(tr[x].tag)
        {
            int L=tr[x].l,R=tr[x].r;
            if(L)
            {
                tr[L].val+=tr[x].tag;
                tr[L].tag+=tr[x].tag;
            }
            if(R)
            {
                tr[R].val+=tr[x].tag;
                tr[R].tag+=tr[x].tag;
            }
            tr[x].tag=0;
        }
    }
    inline int rnd(){return rand();}
    int add(int val,int belong)
    {
        int now;
        if(!que.empty())
        {
            now=que.front();
            que.pop();
        }
        else
            now = ++num;
        q[belong].tree_node=now;
        tr[now].l=tr[now].r=Null;
        tr[now].rnd=rnd();
        tr[now].siz=1;tr[now].val=val;tr[now].belong=belong;
        return now;
    }
    void split_val(int x,int k,int &l,int &r)
    {
        if(!x){l=r=Null;return;}
        pushdown(x);
        if(tr[x].val<=k){l=x;split_val(tr[x].r,k,tr[x].r,r);}
        else {r=x;split_val(tr[x].l,k,l,tr[x].l);}
        pushup(x);
    }
    void split_rank(int x,int k,int &l,int &r)
    {
        if(!x){l=r=Null;return;}
        pushdown(x);
        if(tr[tr[x].l].siz+1<=k)
        {l=x;split_rank(tr[x].r,k-tr[tr[x].l].siz-1,tr[x].r,r);}
        else
        {r=x;split_rank(tr[x].l,k,l,tr[x].l);}
        pushup(x);
    }
    int merge(int x,int y)
    {
        if(!x||!y)return x|y;
        if(tr[x].rnd < tr[y].rnd)
        {
            pushdown(x);
            tr[x].r=merge(tr[x].r,y);pushup(x);return x;
        }
        else
        {
            pushdown(y);
            tr[y].l=merge(x,tr[y].l);pushup(y);return y;
        }
    }

    void insert(int val,int belong)
    {
        int l,r;
        split_val(root,val,l,r);
        root = merge(l,merge(add(val,belong),r));
    }

    int find_node_rank(int x)
    {
        int ans = tr[tr[x].l].siz+1;
        while(x!=root)
        {
            int fa=tr[x].fa;
            if(x==tr[fa].r)ans+=tr[tr[fa].l].siz+1;
            x=fa;
        }
        return ans;
    }

    int Delete_node(int x)
    {
        int rk=find_node_rank(x);
        int l,r,mid;
        split_rank(root,rk-1,l,r);
        split_rank(r,1,mid,r);
        que.push(mid);
        root=merge(l,r);
        return mid;
    }

    void dfs_mid(int x,int val,int &l)
    {
        pushdown(x);
        if(tr[x].l)dfs_mid(tr[x].l,val,l);
        if(tr[x].r)dfs_mid(tr[x].r,val,l);
        tr[x].l=tr[x].r=Null;
        tr[x].siz=1;tr[x].val-=val;//tag?
        int L,R;
        split_val(l,tr[x].val,L,R);
        l=merge(L,merge(x,R));
    }

    void modify(int val)
    {
        int l,mid,r;
        split_val(root,val-1,l,r);
        split_val(r,val+val-1,mid,r);
        if(r)
        {
            tr[r].val-=val;
            tr[r].tag-=val;
        }
        if(mid)dfs_mid(mid,val,l);
        root=merge(l,r);
    }

}treap;
void init()
{
    vec.clear();rev_vec.clear();
    for(int i=1;i<=n;i++)
    {
        pre[i]=0;
        son[i]=fa[i]=0;
    }
    tot=0;
    num=0;
    treap.init();
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&p[i]);
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        dfs1(1);dfs2(1);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&q[i].st,&q[i].ed,&q[i].w);
            LCA(q[i].st,q[i].ed,i);
        }
        sort(vec.begin(),vec.end(),cmp);
        sort(rev_vec.begin(),rev_vec.end(),rev_cmp);
        for(int i=n,j=0;i>=1;i--)
        {
            while(j<rev_vec.size() && rev_vec[j].loc == i && rev_vec[j].is_st)
            {
                treap.insert(q[rev_vec[j].belong].w,rev_vec[j].belong);
                j++;
            }
            treap.modify(p[anti_dfn[i]]);
            while(j<rev_vec.size() && rev_vec[j].loc == i && !rev_vec[j].is_st)
            {
                int nd=treap.Delete_node(q[rev_vec[j].belong].tree_node);
                q[treap.tr[nd].belong].w = treap.tr[nd].val;
                j++;
            }
        }

        for(int i=1,j=0;i<=n;i++)
        {
            while(j<vec.size() && vec[j].loc == i && vec[j].is_st)
            {
                treap.insert(q[vec[j].belong].w,vec[j].belong);
                j++;
            }
            treap.modify(p[anti_dfn[i]]);
            while(j<vec.size() && vec[j].loc == i && !vec[j].is_st)
            {
                int nd=treap.Delete_node(q[vec[j].belong].tree_node);
                q[vec[j].belong].w = treap.tr[nd].val;
                j++;
            }
        }

        for(int i=1;i<=m;i++)
        {
            printf("%d\n",q[i].w);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值