点分治

lhm_

2019-11-27 18:50:22

Personal

点分治用来处理树上路径问题,每一次将树分治为几棵子树,然后继续递归,得到答案

每次分治时,子树的根选取为其的重心,递归的子树大小不会超过原树大小的一半,保证了时间复杂度为O(n\ log\ n)

利用容斥原理统计答案

树上有多少对点,满足两点间的距离小于等于k

code:
void dfs_root(int x,int fa)
{
    siz[x]=1,ma[x]=0;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(vis[y]||y==fa) continue;
        dfs_root(y,x);
        siz[x]+=siz[y];
        ma[x]=max(ma[x],siz[y]);
    }
    ma[x]=max(ma[x],tot-siz[x]);
    if(ma[x]<ma[root]) root=x;
}
void dfs_dis(int x,int fa)
{
    len[++len_cnt]=dis[x];
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to,v=e[i].v;
        if(vis[y]||y==fa) continue;
        dis[y]=dis[x]+v;
        dfs_dis(y,x);
    }
}
int calc(int x,int lenth)
{
    len_cnt=0,dis[x]=lenth;
    dfs_dis(x,0);
    sort(len+1,len+len_cnt+1);
    int sum=0,l=1,r=len_cnt;
    while(l<r)
    {
        if(len[l]+len[r]<=k) sum+=r-l,l++;
        else r--;
    }
    return sum;
}
void solve(int x)
{
    vis[x]=true,ans+=calc(x,0);
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to,v=e[i].v;
        if(vis[y]) continue;
        ans-=calc(y,v);
        root=0,tot=siz[y];
        dfs_root(y,0);
        solve(root);
    }
}

......

tot=ma[0]=n;
dfs_root(1,0);
solve(root);

动态点分治

需要支持修改时,考虑用动态点分治解决

通过构建一棵点分树,然后在点分树上修改,通过不断向上来跳父亲来维护信息

点分树为点分治时每一层的重心之间连边的一棵树,高度为log\ n

捉迷藏:给定一棵树,树上的点为黑色或白色,初始都为黑色,支持修改颜色和询问两个距离最远的黑点的距离

维护三个堆:

$q2$:维护一个节点到在点分树上所有儿子的子树内所有黑点的最大距离(节点所有儿子$q3$的堆顶) $q3$:维护一个节点到在点分树中其子树内所有黑点到该节点父亲的距离 $q2$的存在防止了最大值和次大值的合并时,两条链来自同一子树 $code:
void dfs_root(int x,int fa)
{
    size[x]=1,ma[x]=0,s[++cnt]=x;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(vis[y]||y==fa) continue;
        dfs_root(y,x);
        size[x]+=size[y];
        ma[x]=max(ma[x],size[y]);
    }
    ma[x]=max(ma[x],tot-size[x]);
    if(ma[x]<ma[root]) root=x;
}
void solve(int x)
{
    vis[x]=true;
    q2[x].push(0);
    for(int i=1;i<=cnt;++i)
        q3[x].push(dis(fa[x],s[i]));
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(vis[y]) continue;
        root=cnt=0,tot=size[y];
        dfs_root(y,x);
        fa[root]=x,y=root;
        solve(root);
        q2[x].push(q3[y].top());
    }
    q1.push(q2[x].len());
}
void modify(int x,int type)
{
    q1.del(q2[x].len());
    if(type) q2[x].del(0);
    else q2[x].push(0);
    q1.push(q2[x].len());
    for(int i=x;fa[i];i=fa[i])
    {
        int l1=q3[i].top(),l2,dist=dis(fa[i],x);
        if(type) q3[i].del(dist);
        else q3[i].push(dist);
        l2=q3[i].top();
        q1.del(q2[fa[i]].len());
        if(l1!=-inf) q2[fa[i]].del(l1);
        if(l2!=-inf) q2[fa[i]].push(l2);
        q1.push(q2[fa[i]].len());
    }
}

点分树树高为 log\ n,所有点的子树和为 n\ log\ n