• 题解 P5314 【[Ynoi2011]ODT】


    原文链接

    一道卡常好题。

    我们考虑暴力做法,我们就可以直接对每个点维护相邻的点的平衡树,修改就暴力修改,查询就直接查询就可以了。

    显然不对

    那么我们怎么优化这个算法呢?

    我们可以先进行树链剖分,然后每个点只维护她的轻儿子的平衡树。

    为什么要怎么做呢?它与暴力有什么区别呢?

    考虑树剖的性质。我们每次修改时,最多经过(logn)条重链,每条链只有链头的父亲的一个轻儿子(即链头)发生了改变。我们暴力删除插入,复杂度是(log^2n)的。

    我们每次询问,就暴力把那个点,她的父亲,她的重儿子插入在查询第(k)小就可以了。

    然后,你告诉我你只有80分?

    好吧,实话实说,这题卡平衡树。如果你用(Splay)(FHQ;Treap)是卡不过去的,不信你试试?试试就逝世,我们要用常数更优秀的平衡树。我这里选择了(WBLT)。然而你还是只有(80)分?,快读加火车头多交几遍暴力*过去(ok)了。

    另外有两个小细节

    1. 一定要看这个点到底有没有重儿子与父亲。

    2. 你在修改跳链,最后跳到一个链上时,一定要看一下是不是跳到了链头,如果是还要做一次额外修改。

    最后,(WBLT)大法吼啊!(orz;lxl)

    (Code)

    struct Node
    {
        int val,siz;
        Node *lc,*rc;
        Node(int siz,int val,Node *lc,Node *rc) : siz(siz),val(val),lc(lc),rc(rc) {}
        Node() {}
    };
    
    struct edge
    {
        int v,nxt;
    }e[maxn<<1];
    int head[maxn],kt,n,id[maxn];
    int dfn[maxn],dep[maxn],son[maxn],siz[maxn],top[maxn],fa[maxn],cnt,a[maxn];
    
    inline int max(const int &x,const int &y) {return x>y?x:y;}
    inline int min(const int &x,const int &y) {return x<y?x:y;}
    inline void add(const int &u,const int &v) {e[++kt]=(edge){v,head[u]};head[u]=kt;}
    
    struct Leafy_Tree
    {
        Node *rt[maxn],*stk[maxn<<2],utk[maxn<<2],*null;
        int utot;
        const int ratio=3;
        #define newnode(a,b,c,d) (&(*stk[utot++]=Node(a,b,c,d)))
        #define merge(a,b) newnode(a->siz+b->siz,b->val,a,b)
        inline void init(int n)
        {
            null=new Node(0,0,0,0);
            for(int i=0;i<n*3;++i)
                stk[i]=&utk[i];
            for(int i=0;i<=n+1;++i)
                rt[i]=newnode(1,2147483647,null,null);
        }
        inline void rotate(Node *u)
        {
            if(u->lc->siz > u->rc->siz*ratio)
                u->rc=merge(u->lc->rc,u->rc),stk[--utot]=u->lc,u->lc=u->lc->lc;
            else if(u->rc->siz > u->lc->siz*ratio)
                u->lc=merge(u->lc,u->rc->lc),stk[--utot]=u->rc,u->rc=u->rc->rc;
        }
        inline void pushup(Node *u)
        {
            if(!u->lc->siz) return;
            u->siz=u->lc->siz+u->rc->siz;
            u->val=u->rc->val;
        }
        inline void insert(Node *u,int val)
        {
            if(u->siz==1)
            {
                u->lc=newnode(1,min(val,u->val),null,null);
                u->rc=newnode(1,max(val,u->val),null,null);
            }
            else insert(val > u->lc->val ? u->rc : u->lc,val);
            pushup(u);rotate(u);
        }
        inline void erase(Node *u,int val)
        {
            if(u->lc->siz==1&&u->lc->val==val)
                stk[--utot]=u->lc,stk[--utot]=u->rc,*u=*u->rc;
            else if(u->rc->siz==1&&u->rc->val==val)
                stk[--utot]=u->lc,stk[--utot]=u->rc,*u=*u->lc;
            else erase(val > u->lc->val ? u->rc : u->lc,val);
            pushup(u);rotate(u);
        }
        inline int kth(Node *u,int k)
        {
            if(u->siz==1) return u->val;
            return k > u->lc->siz ? kth(u->rc,k-u->lc->siz) : kth(u->lc,k);
        }
    }leafy;
    
    struct BIT
    {
        int sum[maxn];
        #define lowbit(x) (x&(-x))
        inline void modify(int x,int val) {while(x<=n+5) sum[x]+=val,x+=lowbit(x);}
        inline int query(int x) {int res=0;while(x) res+=sum[x],x-=lowbit(x);return res;}
    }bit;
    
    void dfs1(int u,int f)
    {
        fa[u]=f;dep[u]=dep[f]+1;siz[u]=1;
        for(int i=head[u];i;i=e[i].nxt)
        if(e[i].v!=f)
        {
            dfs1(e[i].v,u);siz[u]+=siz[e[i].v];
            if(siz[e[i].v]>siz[son[u]]) son[u]=e[i].v;
        }
    }
    
    void dfs2(int u,int topf)
    {
        top[u]=topf;dfn[u]=++cnt;id[cnt]=u;
        if(!son[u]) return;
        dfs2(son[u],topf);
        for(int i=head[u];i;i=e[i].nxt)
            if(e[i].v!=fa[u]&&e[i].v!=son[u])
                dfs2(e[i].v,e[i].v),leafy.insert(leafy.rt[u],a[e[i].v]);
    }
    
    inline void modify(int u,int v,int val)
    {
        while(top[u]^top[v])
        {
            if(dep[top[u]]<dep[top[v]]) u^=v^=u^=v;
            if(fa[top[u]]) leafy.erase(leafy.rt[fa[top[u]]],bit.query(dfn[top[u]]));
            bit.modify(dfn[top[u]],val),bit.modify(dfn[u]+1,-val);
            if(fa[top[u]]) leafy.insert(leafy.rt[fa[top[u]]],bit.query(dfn[top[u]]));
            u=fa[top[u]];
        }
        if(dep[u]>dep[v]) u^=v^=u^=v;
        if(u==top[u]&&fa[u]) leafy.erase(leafy.rt[fa[u]],bit.query(dfn[u]));
        bit.modify(dfn[u],val);bit.modify(dfn[v]+1,-val);
        if(u==top[u]&&fa[u]) leafy.insert(leafy.rt[fa[u]],bit.query(dfn[u]));
    }
    
    inline int query(int u,int k)
    {
        int valu=bit.query(dfn[u]),valf,vals,ans;
        leafy.insert(leafy.rt[u],valu);
        if(fa[u]) valf=bit.query(dfn[fa[u]]),leafy.insert(leafy.rt[u],valf);
        if(son[u]) vals=bit.query(dfn[son[u]]),leafy.insert(leafy.rt[u],vals);
        ans=leafy.kth(leafy.rt[u],k);
        leafy.erase(leafy.rt[u],valu);
        if(fa[u]) leafy.erase(leafy.rt[u],valf);
        if(son[u]) leafy.erase(leafy.rt[u],vals);
        return ans;
    }
    
    int main()
    {
        int opt,x,y,z,t;
        read(n);read(t);
        for(int i=1;i<=n;++i)
            read(a[i]);
        for(int i=1;i<n;++i)
        {
            read(x);read(y);
            add(x,y);add(y,x);
        }
        leafy.init(n);
        dfs1(1,0);dfs2(1,1);
        for(int i=1;i<=n;++i)
            bit.modify(i,a[id[i]]),bit.modify(i+1,-a[id[i]]);
        while(t--)
        {
            read(opt);read(x);read(y);
            if(opt==1) read(z),modify(x,y,z);
            else write(query(x,y),'
    ');
        }
        flush();
        return 0;
    }
    
  • 相关阅读:
    对于指定区块div,如何区分区块内的点击 和 区块外的点击?
    broadcom代码中httpd进程启动流程介绍
    一个简单的搜索布局样式
    一种在视频OBJECT标签上放置均分四个区域的框选方法
    JQuery执行DOM批量克隆并插入的提效方法
    DevOps技术路线图
    后端开发技术路线图
    Angular route传参
    Angular使用echarts
    TypeScript Array Remove
  • 原文地址:https://www.cnblogs.com/gxm123/p/13604666.html
Copyright © 2020-2023  润新知