点分树略记

前置知识是点分治。点分树就是动态点分治。

将点分治的递归过程建成树,每个分治中心(重心)向上一层分治中心连边,形成一棵树的结构。

树高是 \(O(\log n)\) 的,这意味着我们可以暴力向上跳分治中心。

子树大小之和是 \(O(n \log n)\) 的,这意味着我们可以给每个分治中心都开一个数据结构,用数据结构维护每一层的答案,还可以支持修改操作。

建树过程就是做一次点分治。

int divfa[maxn];
void divide(int u)
{
    vis[u]=true;
    for(int i=head[u];~i;i=e[i].nxt){
        int v=e[i].v;
        if(vis[v]) continue;
        U=sz[v],rt=-1,getrt(v,u);
        divfa[rt]=u,divide(rt);
    }
}

每次询问暴力向上跳分治中心,计算以询问点为端点,跨过当前分治中心的答案。

先求出另一端点在当前分治中心管辖范围内的答案,但是这样会多算一部分,也就是另一端点和询问点没有跨过分治中心,要减去这部分的答案。

如模板题,用两棵线段树维护两部分,再相减。

int qry(int u,int k){
    int res=0;
    for(int nw=u,pre=0;nw;pre=nw,nw=divfa[nw]){
        int d=k-dis(u,nw);
        if(d<0) continue;
        res+=A.query(nw,d);
        if(pre) res-=B.query(pre,d);
    }
    return res;
}

修改也是非常简单地向上暴力跳就好了,一个点会被上面所有分治中心管辖,依次修改 \(O(\log n)\) 次。

void upd(int u,int val){
    for(int nw=u;nw;nw=divfa[nw]){
        A.modify(nw,dis(nw,u),val);
        if(divfa[nw]) B.modify(nw,dis(divfa[nw],u),val);
    }
}

如果要实现类似于“在 lca 合并路径”的操作,如 Qtree IV,可以用三个可删堆维护,维护在每个分治中心合并的情况:

struct Heap{
    priority_queue<int> x,y;
    void Push(int t){ x.push(t); }
    void Del(int t){ y.push(t); }
    void adjust(){ while(!y.empty() && x.top()==y.top()) x.pop(),y.pop(); }
    void Pop(){ adjust(); if(x.size()) x.pop(); }

    int Sz(){ return x.size()-y.size(); }
    int Top(){ if(!Sz()) return -inf; adjust(); return x.top(); }
    int Sec(){ if(Sz()<2) return -inf; int t=Top(); Pop(); int t2=Top(); Push(t); return t2; }
}hp[maxn],hp2[maxn],ans;

void addval(int u,int x){
    if(hp[u].Sz()>=2)
        ans.Del(hp[u].Top()+hp[u].Sec());
    hp[u].Push(x);
    if(hp[u].Sz()>=2)
        ans.Push(hp[u].Top()+hp[u].Sec());
}
void delval(int u,int x){
    if(hp[u].Sz()>=2)
        ans.Del(hp[u].Top()+hp[u].Sec());
    hp[u].Del(x);
    if(hp[u].Sz()>=2)
        ans.Push(hp[u].Top()+hp[u].Sec());
}
void Add(int u)
{
    addval(u,0);
    for(int nw=u;dfa[nw];nw=dfa[nw])
    {  
        int presz=hp2[nw].Sz(),pretop=hp2[nw].Top();
        int d=dis(u,dfa[nw]);
        if(!presz || d>pretop){
            if(presz) delval(dfa[nw],pretop);
            addval(dfa[nw],d);
        }
        hp2[nw].Push(d);
    }
}
void Del(int u)
{
    delval(u,0);
    for(int nw=u;dfa[nw];nw=dfa[nw])
    {
        int pretop=hp2[nw].Top();
        int d=dis(u,dfa[nw]);

        hp2[nw].Del(d);
        if(pretop==d){
            delval(dfa[nw],d);
            if(hp2[nw].Sz()) addval(dfa[nw],hp2[nw].Top());
        }
    }
}
posted @ 2025-04-30 16:03  wanggk  阅读(25)  评论(0)    收藏  举报