全局平衡二叉树

前几天学了全局平衡二叉树,发现这玩意非常牛逼。

现在我们想要在一棵树上链修改链查询,而我又不想要 \(O(n\log^2n)\) 的树剖线段树,更不想要常数大的要死的 \(O(n\log n)\) Lct,这时候全局平衡二叉树的小常数 \(O(n\log n)\) 就变得非常优越了。

我们仍然考虑树剖剖出来的重链,对每条重链建出来二叉查找树,满足一条重链的二叉查找树的中序遍历是按深度递增的,这样会使我们在重链上变成区间修改,在新的全局平衡树上我们通过轻边把两条重链连在一起,满足认父不认子,修改和查询的时候我们条重链,然后在二叉查找树上定位区间就可以了,复杂度是 \(O(n\log n)\)

建树代码

int tbuild(int l,int r,int rt)
{
    int L = l,R = r,mid;
    while (L <= R)
    {
        mid = L + R >> 1;
        if (s[mid] - s[L - 1] <= s[R] - s[mid])
            L = mid + 1;
        else
            R = mid - 1;
    }
    int u = q[L];
    if (!rt)
        rt = u;
    bel[u] = rt;
    sz[u] = r - l + 1;
    if (L > l)
        lc[u] = tbuild(l,L - 1,rt);
    if (L < r)
        rc[u] = tbuild(L + 1,r,rt);
    return u;
}
void build(int x)
{
    int u = x;
    while (u)
    {
        for (int i = head[u];i;i = nxt[i])
        {
            int v = edge[i];
            if (v == son[u])
                continue;
            build(v);
        }
        u = son[u];
    }
    int cnt = 0;u = x;
    while (u)
    {
        q[++cnt] = u;
        s[cnt] = s[cnt - 1] + size[u] - size[son[u]];
        top[u] = x;
        u = son[u];
    }
    u = tbuild(1,cnt,0);
}

tbuild 中是求出重链的加权中点作为根,比较优。

然后修改操作跟树剖是差不多的

void update(int k,int l,int r,int w)
{
    if (l == 1 && r == sz[k])
    	return (void)(upd(k,w));
    int mid = sz[lc[k]];
    pushdown(k);
    if (l <= mid && lc[k])
        update(lc[k],l,min(mid,r),w);
    if (r > mid + 1 && rc[k])
        update(rc[k],max(1,l - mid - 1),r - mid - 1,w);
    if (l <= mid + 1 && r > mid)
        val[k] += w,sm[k] += w;
    pushup(k);
}
void modify(int x,int y,int w)
{
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]])
            swap(x,y);
        update(bel[x],1,dep[x] - dep[top[x]] + 1,w);
        x = fa[top[x]];
    }
    if (dep[x] > dep[y])
        swap(x,y);
    update(bel[x],dep[x] - dep[top[x]] + 1,dep[y] - dep[top[y]] + 1,w);
}

最后是查询操作

long long ask(int k,int l,int r)
{
    if (!k)return 0;
    if (l == 1 && r == sz[k])
        return sm[k];
    int mid = sz[lc[k]];
    long long ans = 0;
    pushdown(k);
    if (l <= mid)
        ans += ask(lc[k],l,min(mid,r));
    if (r > mid + 1)
        ans += ask(rc[k],max(1,l - mid - 1),r - mid - 1);
    if (l <= mid + 1 && r > mid)
        ans += val[k];
    return ans;
}
long long query(int x,int y)
{
    long long ans = 0;
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]])
            swap(x,y);
        ans += ask(bel[x],1,dep[x] - dep[top[x]] + 1);
        x = fa[top[x]];
    }
    if (dep[x] > dep[y])
        swap(x,y);
    ans += ask(bel[x],dep[x] - dep[top[x]] + 1,dep[y] - dep[top[y]] + 1);
    return ans;
}
posted @ 2021-03-16 21:39  eee_hoho  阅读(431)  评论(0编辑  收藏  举报