LGP6329 [LG TPLT] 点分治·II 学习笔记

LGP6329 [LG TPLT] 点分治·II 学习笔记

Luogu Link

题意简述

\(n\) 个结点的树,边有边权,\(m\) 次操作:

  • 0 u k 输出所有与 \(u\) 距离不大于 \(k\) 的点的权值和。
  • 1 u k 单点修改,\(v_u=k\)

强制在线。

做法解析

树形态是不变的。你不希望每次询问都重复一遍把递归下去的过程,所以你考虑建出一棵点分树。点分树就是沿递归子问题的结点连边所得。好点分树讲完了。

呃等下,回到这个问题本身,这个权值和怎么求解呢?

所有与 \(u\) 距离不超过 \(k\) 的权值和?在点分树上?因为我们知道点分树深度为 \(O(\log n)\),所以我们可以考虑直接暴力跳:对于点分树上从 \(u\) 开始往祖先的直链上的每一个点 \(v\),设 \(\text{dis}(u,v)=d\),则我们每次就把答案加上 \(v\) 点分树子树里距离 \(v\) 不超过 \(k-d\) 的点。

但是这会算重。为了去重,对于每一对直链上的父子 \((i,j)\),我们在加上 \(i\) 子树里距自己不超过 \(k-d\) 的点权值和的同时,还要减去 \(j\) 子树里距 \(i\) 不超过 \(k-d\) 的点权值和。

然后用树状数组就行。因为这是点分树,所以你对于每个结点开 \(siz\) 大小的空间可以接受。

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=1e5+5;
int N,M,A[MaxN],X,Y,Opt;
vector<int> Tr[MaxN],Dr[MaxN];
void addudge(int u,int v){
    Tr[u].push_back(v);
    Tr[v].push_back(u);
}
int dep[MaxN],siz1[MaxN],tfa[MaxN],hvs[MaxN];
void dfs1(int u,int f){
    dep[u]=dep[f]+1,siz1[u]=1,tfa[u]=f;
    for(int v : Tr[u]){
        if(v==f)continue;
        dfs1(v,u),siz1[u]+=siz1[v];
        if(siz1[v]>siz1[hvs[u]])hvs[u]=v;
    }
}
int top[MaxN];
void dfs2(int u,int t){
    top[u]=t;if(hvs[u])dfs2(hvs[u],t);
    for(int v : Tr[u])if(v!=tfa[u]&&v!=hvs[u])dfs2(v,v);
}
int getlca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=tfa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
int getdis(int x,int y){return dep[x]+dep[y]-2*dep[getlca(x,y)];}
int siz2[MaxN],hsw[MaxN],vis[MaxN];
int getroot(int u,int f,int tot){
    siz2[u]=1,hsw[u]=0;int t=0,x;
    for(int v : Tr[u]){
        if(v==f||vis[v])continue;
        x=getroot(v,u,tot);if(hsw[x]<hsw[t])t=x;
        siz2[u]+=siz2[v],maxxer(hsw[u],siz2[v]);
    }
    maxxer(hsw[u],tot-siz2[u]);
    if(hsw[u]<hsw[t])t=u;
    return t;
}
struct BinidTree{
    int n;vector<int> t;
    int lowbit(int x){return x&(-x);}
    void init(int x){t.clear(),n=x,t.resize(n+1);}
    void add(int p,int x){p++;for(;p<=n;p+=lowbit(p))t[p]+=x;}
    int gts(int p){
        p++,minner(p,n);int res=0;
        for(;p;res+=t[p],p-=lowbit(p));return res;
    }
}BiT[MaxN][2];
int dfa[MaxN];
void build(int u,int s,int utot){
    vis[u]=1;
    BiT[u][0].init(utot+1);
    BiT[u][1].init(utot+1);
    for(int v : Tr[u]){
        if(vis[v])continue;
        int vtot=siz2[v]<siz2[u]?siz2[v]:siz2[s]-siz2[u];
        int rt=getroot(v,0,vtot);dfa[rt]=u,build(rt,v,vtot);
    }
}
void abuild(){hsw[0]=N,build(getroot(1,0,N),1,N);}
void modify(int u,int w){
    for(int i=u;i;i=dfa[i])BiT[i][0].add(getdis(u,i),w);
    for(int i=u;dfa[i];i=dfa[i])BiT[i][1].add(getdis(u,dfa[i]),w);
}
int ans;
int acount(int u,int k){
    int res=BiT[u][0].gts(k);
    for(int i=u;dfa[i];i=dfa[i]){
        int tdis=k-getdis(u,dfa[i]);
        if(tdis>=0)res+=BiT[dfa[i]][0].gts(tdis)-BiT[i][1].gts(tdis);
    }
    return res;
}
void aupdate(int u,int k){modify(u,k-A[u]),A[u]=k;}
int main(){
    readis(N,M);
    for(int i=1;i<=N;i++)readi(A[i]);
    for(int i=1;i<N;i++)readis(X,Y),addudge(X,Y);
    dfs1(1,0),dfs2(1,1);abuild();
    for(int i=1;i<=N;i++)modify(i,A[i]);
    for(int i=1;i<=M;i++){
        readis(Opt,X,Y);
        X^=ans,Y^=ans;
        if(Opt==0)ans=acount(X,Y),writil(ans);
        if(Opt==1)aupdate(X,Y);
    }
    return 0;
}
posted @ 2025-08-10 01:27  矞龙OrinLoong  阅读(15)  评论(0)    收藏  举报