点分树学习笔记

点分树是基于点分治的支持修改操作的算法,所以也叫动态点分治。

具体来说,我们发现对于某些树上的题目,点与点之间的距离十分重要,点与点之间的关系反而不那么重要。比如查询距离 uk 的点。

由于点分治是一种离线做法,而很多毒瘤出题人往往会加上修改/强制在线,这时候就用到点分树了。

考虑点分治之后我们将每个点分中心连向它的上级点分中心。可以发现,由于点分治的性质,这样建出来的树的树深一定是 O(logn) 的。

这意味着我们可以跳 fa 把一个暴力做法优化到 O(nlog2n)

但是这样会有一个问题:如果我们对每个点分中心(及其祖先)都记录,那么势必会有一些点会被计算多次。

比如上图显然点分治中心是 1,然后二层分治中心是 2,5。可以发现,如果我们查询距离4号点 4以内的点,那么首先会查询距离2号点3以内的点,再查询距离1号点2以内的点。可以发现这里就会被记重。

比如答案英应该是蓝色区域与红色区域的并,但是我们会把交统计两遍。

所以考虑容斥。不妨专门开一个数组统计点分树上的父亲在当前点分子树的贡献,即上图中蓝色区域与红色区域的交。

我们把所有答案加起来,再去掉这部分贡献即可。

模板:bzoj3730震波

题目大意

给定一棵树,点有点权,支持单点修改,查询距离某一点 u 不超过 k 的点权和。

题解

建出点分树。可以发现,我们要做的就是单点修改,维护前缀和。

这个直接用线段树/树状数组维护一下即可。特别的,这里需要用到上述差分技巧,即 u 应当减去已经处理过的子树 v 的贡献。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100010
using namespace std;
int nxt[N<<1],to[N<<1],head[N],cnt;
void add(int u,int v)
{
    nxt[++cnt]=head[u];
    to[cnt]=v;
    head[u]=cnt;
}
namespace ST{
    int f[21][N*2],dep[N*2],id[N*2],tot;
    int _2[N*2];
    void dfs(int u,int p)
    {
        dep[u]=dep[p]+1;
        f[0][++tot]=u;
        id[u]=tot;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(v==p) continue;
            dfs(v,u);
            f[0][++tot]=u;
        }
    }
    int lca(int x,int y)
    {
        x=id[x],y=id[y];
        if(x>y) swap(x,y);
        int p=_2[y-x+1],u=1<<p;
        return dep[f[p][x]]>dep[f[p][y-u+1]]?f[p][y-u+1]:f[p][x];
    }
    void init()
    {
        dfs(1,0);
        for(int i=2;i<=tot;i++) _2[i]=_2[i>>1]+1;
        for(int i=1,p=2;p<=tot;i++,p<<=1)
            for(int j=1;j+p-1<=tot;j++)
            if(dep[f[i-1][j]]<dep[f[i-1][j+p/2]]) f[i][j]=f[i-1][j];
            else f[i][j]=f[i-1][j+p/2];
    }
}
int dist(int x,int y){return ST::dep[x]+ST::dep[y]-2*ST::dep[ST::lca(x,y)];}
int ssiz[N],maxs[N],rt;
bool vis[N];
void dfs1(int u,int f,int all)
{
    ssiz[u]=1;
    maxs[u]=0;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(vis[v] || v==f) continue;
        dfs1(v,u,all);
        maxs[u]=max(maxs[u],ssiz[v]);
        ssiz[u]+=ssiz[v];
    }
    maxs[u]=max(maxs[u],all-ssiz[u]);
    if(!rt || maxs[u]<maxs[rt]) rt=u;
}
int siz[N],fa[N];
void dfs2(int u,int all)
{
    vis[u]=true;
    siz[u]=all;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(vis[v]) continue;
        int vz=ssiz[v]<ssiz[u]?ssiz[v]:all-ssiz[u];
        rt=0;
        dfs1(v,u,vz);
        fa[rt]=u;
        dfs2(rt,vz);
    }
}
int root[N],droot[N],ls[N*40],rs[N*40],tcnt,val[N*40];
void insert(int &u,int l,int r,int p,int v)
{
    if(!u) u=++tcnt;
    val[u]+=v;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(p<=mid) insert(ls[u],l,mid,p,v);
    else insert(rs[u],mid+1,r,p,v);
}
int answer(int u,int l,int r,int L,int R)
{
    if(L<=l && r<=R) return val[u];
    if(!u || L>R) return 0;
    int mid=(l+r)>>1,a=0;
    if(L<=mid) a+=answer(ls[u],l,mid,L,R);
    if(R>mid) a+=answer(rs[u],mid+1,r,L,R);
    return a;
}
void change(int s,int v)
{
    for(int p=s;p;p=fa[p])
    {
        insert(root[p],0,siz[p],dist(s,p),v);
        if(fa[p]) insert(droot[p],0,siz[fa[p]],dist(fa[p],s),v);
    }
}
int answer(int s,int k)
{
    int res=0;
    for(int u=s,p=0;u;p=u,u=fa[u])
    {
        int d=dist(u,s);
        if(d>k) continue;
        res+=answer(root[u],0,siz[u],0,k-d);
        if(p) res-=answer(droot[p],0,siz[u],0,k-d);
    }
    return res;
}
int w[N];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    ST::init();
    dfs1(1,0,n);
    dfs2(rt,n);
    for(int i=1;i<=n;i++) change(i,w[i]);
    int las=0;
    for(int i=1;i<=m;i++)
    {
        int opt,x,y;
        scanf("%d%d%d",&opt,&x,&y);
        x^=las,y^=las;
        if(opt==0) printf("%d\n",las=answer(x,y));
        else change(x,y-w[x]),w[x]=y;
    }
    return 0;
}
posted @   Flying2018  阅读(241)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示