[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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章