[JZOJ5091]绝版题

题目大意

有一棵树,一开始只有一个节点1 ,权值为t
接下来会有q 个操作,操作有以下三种:
  插入一个节点,编号是当前点数+1 ,父亲节点是x ,权值是y
  将节点x 的权值修改为y
  询问这棵树的带权重心的编号,如果有两个,那么选择离1 号节点最近的那个
本题强制在线。

1q3×105 ,其中插入操作和修改操作之和不超过1.5×105


题目分析

既然有动态改变树的形态,那么考虑使用LCT。
怎么在LCT上找带权重心呢?既然要求是离1 号节点最近的一个,我们考虑从1 开始沿着偏好链往下走。如果我遇到一个点它的偏好儿子的子树权值和没有超过总和的一半,我就不能再往偏好链走了,这时有两种可能:要么这个点是带权重心,要么带权重心在这个点权值和最大的虚儿子子树内。我对每一个点开一个set 维护一下所有虚儿子的子树点权和就可以轻松判断或者求出最大的虚儿子。如果依然不是重心我就对虚儿子做上面的过程直到找到重心为止。最后把重心access 一下,复杂度就和access 一致了。
至于修改操作,我直接access 这个点然后在splay 上打上tag就好了。
注意虚实边切换的细节要想好,不然很容易错。
时间复杂度O(nlog2n) (虚实边切换是O(logn) 次的,每一次都要更新set )。


代码实现

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <stack>
#include <set>

using namespace std;

typedef long long LL;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int buf[30];

void write(int x)
{
    if (x<0) putchar('-'),x=-x;
    for (;x;x/=10) buf[++buf[0]]=x%10;
    if (!buf[0]) buf[++buf[0]]=0;
    for (;buf[0];putchar('0'+buf[buf[0]--]));
}

const int N=150050;

set<LL> heap[N];
LL all;
int n,q,ans;
int p[N];
bool is_online;

namespace link_cut_tree
{
    int fa[N],par[N],size[N],LEFT[N];
    LL v[N],mx[N],tag[N];
    int son[N][2];
    stack<int> st;

    bool side(int x){return son[fa[x]][1]==x;}

    void update(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1,LEFT[x]=son[x][0]?LEFT[son[x][0]]:x,mx[x]=max(v[x],max(mx[son[x][0]],mx[son[x][1]]));}

    void ADD(int x,LL delta){v[x]+=delta,mx[x]+=delta,tag[x]+=delta;}

    void clear(int x)
    {
        if (tag[x])
        {
            if (son[x][0]) ADD(son[x][0],tag[x]);
            if (son[x][1]) ADD(son[x][1],tag[x]);
            tag[x]=0;
        }
    }

    void pushdown(int x,int y)
    {
        for (;x!=y;st.push(x),x=fa[x]);
        for (;!st.empty();clear(st.top()),st.pop());
    }

    void rotate(int x)
    {
        int y=fa[x];bool s=side(x);
        if (fa[y]) son[fa[y]][side(y)]=x;
        if (son[x][s^1]) fa[son[x][s^1]]=y;
        son[y][s]=son[x][s^1],son[x][s^1]=y;
        fa[x]=fa[y],fa[y]=x;
        if (par[y]) par[x]=par[y],par[y]=0;
        update(y),update(x);
    }

    void splay(int x,int y)
    {
        for (pushdown(x,y);fa[x]!=y;rotate(x))
            if (fa[fa[x]]!=y)
                if (side(x)==side(fa[x])) rotate(fa[x]);
                else rotate(x);
    }

    int search(int x)
    {
        clear(x);
        if (mx[son[x][1]]<<1>all) return search(son[x][1]);
        return v[x]<<1>all?x:search(son[x][0]);
    }

    int getlight(int x,int y){return splay(y=LEFT[y],x),heap[x].insert((v[y]<<20)+y),y;}

    int getheavy(int x,int y){return splay(y=LEFT[y],0),heap[x].erase((v[y]<<20)+y),y;}

    int access(int x)
    {
        int nxt=0;
        for (;x;update(nxt=x),x=par[x])
        {
            splay(x,0);
            if (son[x][1]) son[x][1]=getlight(x,son[x][1]),par[son[x][1]]=x,fa[son[x][1]]=0;
            if (nxt) nxt=getheavy(x,nxt),par[nxt]=0,fa[nxt]=x;
            son[x][1]=nxt;
        }
        return nxt;
    }

    void modify(int x,int delta){ADD(access(x),delta),all+=delta;}

    void addcity(int x,int y){p[++n]=y,par[n]=x,update(n),modify(n,y);}

    int core()
    {
        int ret=0;
        for (int x=1,y;!ret;x=y)
        {
            splay(x,0),x=search(x);
            LL get=heap[x].empty()?0:(*heap[x].rbegin());
            if ((get>>20)<<1<=all) ret=x;
            y=get&((1<<20)-1);
        }
        return access(ret),ret;
    }
};

int main()
{
    using namespace link_cut_tree;
    freopen("jueban.in","r",stdin),freopen("jueban.out","w",stdout);
    for (is_online=1,q=read(),addcity(0,read());q--;)
    {
        int tp=read(),x,y;
        switch (tp)
        {
            case 1:x=read()^(ans*is_online),y=read()^(ans*is_online),addcity(x,y);/*printf("1 %d %d\n",x,y);*/break;
            case 2:x=read()^(ans*is_online),y=read()^(ans*is_online),modify(x,y-p[x]),p[x]=y;/*printf("2 %d %d\n",x,y);*/break;
            default:write(ans=core()),putchar('\n');
        }
    }
    fclose(stdin),fclose(stdout);
    return 0;
}
发布了227 篇原创文章 · 获赞 42 · 访问量 19万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章