Location>code7788 >text

Tree Partition Family Bucket

Popularity:485 ℃/2024-11-19 10:48:05

Tree Partition Family Bucket

Tree, (It is an environmentally friendly plant) is a special kind of graph among graph theory, due to (The greening of the environment is excellent) Special properties abound and are often found all around us.

This article will focus on (How to Plant a Tree) A kind of graceful violence on trees - tree partitioning.

tree partition

Tree partitioning can reduce some of the violence to\(O(\log n)\) until\(O(\log^2 n)\) level for queries related to paths in the tree and their deformations, queries for a particular class of points in the tree, etc.

Part1 Partitioning

Point partitioning serves as a foundation idea for tree partitioning, primarily utilizing theCenter of gravity of the treeMake constant divisions.

Example 1:P3806 [Template] Point division 1

Violent enumeration starting point handles each query with complexity\(O(n^2)\)

Start with the center of gravity of the tree and find the center of gravity (if there is more than one either way).

In the tree pictured above, the\(2\) The node is the center of gravity of the tree, which we call the first level of the partition center.

in order to\(2\) As a root, find the distances to each point, yielding the distance in terms of\(2\) because ofan endpointThe length of all the paths of the

Now consider one.run through \(2\) paths, such a path can consist of two paths that are not in the\(2\) (e.g., the path\(8\to 10\) path\(2\to8\) integration with pathways\(2\to 10\) (obtained by summing the two paths), and adding these two paths gives a path through the\(2\) The path.

When processing queries, we enumerate all path lengths within a subtree\(dis\), whether the query was previously tagged with a length of\(dis-k\) The paths in one of the other subtrees of the enumeration. Before enumerating the next subtree, mark all path lengths in that subtree.

Now, all the people who cross the\(2\) All paths (including those ending in 2) are considered. Then all subsequent paths have nothing to do with 2 and 2 is simply removed.

The chart changes to:

Now a forest is formed and for each new tree we find the center of gravity of the tree separately, they are\(4,6,5,10\), calling it the second tier of partition centers. For each partition center, repeat the process for\(2\) The operations performed to find the path to process the query, considering theWithin each treePaths through the Partition Center.

Delete the second level of partition centers and repeat the above for the new forest. This is done sequentially until the last node is deleted, at which point the paths of the original tree are all considered by and only by one of the partition centers, with obvious correctness.

Analyzing the time complexity, the sum of nodes traversed by the partition center at each level is\(O(n)\) level, according to the definition of the center of gravity of the tree, the maximum size of the subtree remaining in each partition center after deletion of itsnot greater thanits\(\frac{1}{2}\)It is clear that at best, only\(\log n\) layer nodes, the time complexity of finding path lengths with fixed endpoints reaches an excellent\(O(n\log n)\), since it is followed by the path length of the enumerated subtree with the path length of the query, the bottleneck is the\(O(nm\log n)\)

Example code:

#include<bits/stdc++.h>
using namespace std;

const int maxn=2e4+5;

struct Edge
struct Edge; {
    struct Edge { int tot; int head[maxn]; int head[maxn]; struct Edge {
    int head[maxn]; struct edgenode { int to,nxt,w;}edge[maxn*2]; }
    struct edgenode{int to,nxt,w;}edge[maxn*2].
    inline void add(int x,int y,int z)
    inline void add(int x,int y,int z)
        inline void add(int x,int y,int z) { tot++; edge[tot].toy
        edge[tot].to=y;
        edge[tot].w=z;
        edge[tot].nxt=head[x];
        head[x]=tot;
    }
}T;

int n,m,tot,rt,crem.
int dis[maxn],q[maxn],rem[maxn],mx[maxn],qry[maxn];

bool jug[maxn*maxn],ans[maxn];

int siz[maxn];
bool book[maxn],cut[maxn];
inline void dfs_siz(int u)//find the size of the subtree rooted at u
{
    book[u]=true;siz[u]=1;
    for(int i=[u];i;i=[i].nxt)
    {
        int v=[i].to;
        if(book[v]||cut[v]) continue;
        dfs_siz(v);siz[u]+=siz[v];
    }
    book[u]=false;
}
inline int dfs_rt(int u,const int tot)//return the center of gravity, tot is the total number of nodes in the current tree
{
    book[u]=true;int ret=u;
    for(int i=[u];i;i=[i].nxt)
    {
        int v=[i].to;
        if(book[v]||cut[v]) continue;
        if(siz[v]*2>=tot){ret=dfs_rt(v,tot);break;}
    }
    book[u]=false;return ret;
}
inline void dfs_dis(int u)//find the distance from the center of gravity to u and record it in the array rem
{
    book[u]=true;
    rem[++crem]=dis[u];
    for(int i=[u];i;i=[i].nxt)
    {
        int v=[i].to;
        if(cut[v]||book[v]) continue;
        dis[v]=dis[u]+[i].w;
        dfs_dis(v);
    }
    book[u]=false;
}
inline void calc(int u)//consider the path through the center of gravity
{
    queue<int>que;
    while(! ()) ();
    for(int i=[u];i;i=[i].nxt)//enumerate subtrees
    {
        int v=[i].to;
        if(cut[v]) continue;
        crem=0,dis[v]=[i].w;
        dfs_dis(v);
        for(int j=1;j<=crem;j++)//enumerate path lengths
            for(int k=1;k<=m;k++)//enumerate queries
            {
                if(qry[k]>=rem[j]) ans[k]|=jug[qry[k]-rem[j]];
                // query other subtrees for paths of length qry[k]-rem[j]
            }
        for(int j=1;j<=crem;j++) (rem[j]),jug[rem[j]]=1;// mark the existence of a subtree with a path of length rem[j]
    }
    while(! ()) jug[()]=0,(); }
}
inline void dfs(int u)//handle the new tree where u is located
{
    dfs_siz(u);int g=dfs_rt(u,siz[u]);cut[g]=1;//remove the center of gravity g (marked 1)
    jug[0]=1;// path of g->g, easy to add with other paths to form path of g->x
    calc(g).
    for(int i=[g];i;i=[i].nxt)
    {
        int v=[i].to;
        if(cut[v]) continue;
        dfs(v);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x,y,z; scanf("%d%d",&n,&m) {
        scanf("%d%d%d",&x,&y,&z);
        (x,y,z).
        (x,y,z); (y,x,z).
    }
    for(int i=1;i<=m;i++) scanf("%d",&qry[i]);
    dfs(1);
    for(int i=1;i<=m;i++)
    {
        if(ans[i]) printf("AYE\n");
        else printf("NAY\n");
    }
}

summarize

The essence of the point partition is to partition the graph after fixing the partition center to run a single point of violence, and then merge the answers of different paths, usually applying the enumeration of subtrees to de-emphasize the way.

Of course, this algorithm does not support online processing.

Part2 Border Partition

Edge partitioning is similar to point partitioning, although it is a cold algorithm.

Choose an edge which splits into two sides of the tree of the most uniform size, violently process the information from the two sides of the tree separately, and link the resulting information in through the edge.

But if the diagram is a daisy chart then the side partition will be stuck as a\(O(n^2)\)

What should I do?

Turn the original tree into a binary tree, like this:

Ps: Image quoted from the OI-wiki Tree Partitioning chapter.

Conversion to a binary tree eliminates the drawbacks of daisy charts and makes edge partitioning universal.

Due to the increase of up to\(n\) points, total complexity\(O(n\log n)\)

Most of the questions on point partitioning can also be done on side partitioning.

Part3 Point Split Tree (Dynamic Tree Partitioning)

The ultimate version of tree partitioning, changing the original tree form so that the number of layers becomes\(\log n\) of a reconstructed tree, usually resolved with the original tree morphologyhaving nothing do (with sth else)of band modifications or online issues.

When the point is partitioned, we delete the center of that partition, which forms a number of subtrees, and concatenate the center of that partition with the centers of the partitions of those subtrees to form a reconstructed tree.

According to the time complexity proof of the point partition, the depth of this tree is less than\(\log n\)

Below is an example of what the original tree looks like when it is changed to a reconstructed tree.

The original tree:

post-tree splittingreconstructTree:

It sacrifices the original structure for a high-speed one, so if the subtree has a path (change distance, change all nodes in the subtree at once, disconnect edges, reconnect edges ......) relationship with the parent, it's done.

So the common properties of point-split trees and prototrees are:

  1. A subtree within a point-split tree is a linked block of the original tree.
  2. point-splitting tree with two points on it\(Lca\) on the path of the two points of the original tree.
  3. A partition center has two-by-two subtrees except for the one up from the original tree\(Lca\) for themselves.

There's a lot of counterintuition in point-splitting trees:

  1. point pair in a point-split tree\((u,v)\) The distance between is independent of its distance on the prototree and its distance on the point-split tree (note in particular that the prototree distance to the ancestor is also independent of the distance on the point-split tree).
  2. None of the ancestry and sibling relationships on the point-split tree are related to the original tree.

The most important thing about it is that it supports changing some of the information online!

And what can this tree do?

Example 2:P6329 [Template] Point Split Tree | Shockwave

Violently, traversing the surrounding distance up to\(k\) points, the time complexity\(O(qn)\)

The following unspecified trees are all point-split trees, with nodes in each tree\(u\)set up\(w[u][i]\) is the distance from the original tree\(u\) because of\(i\) The sum of the weights of the points on the point-split tree that belong to its own subtree.

Formalized.\(w[u][i]=\sum_{v\in } [dis(u,v)==i]\times val[v]\)where the subtree is a point-split tree of\(u\) Subtree, distance from\(dis(u,v)\) because of\(u,v\) Distance from the original tree (below).

insofar as\(u\) of the query from a node in the point-split tree\(u\) To begin, first query\(\sum_{i=0}^k w[u][i]\). At this point, the point-split tree\(u\) of the subtree is then queried, i.e., corresponds, and the points within a connected block in the original tree are computed with the\(u\) Contribution.

commander-in-chief (military)\(u\) Jump up to the father (in this case the father on the point-split tree, below) and query the\(\sum_{i=0}^{k-dis(u,fa_u)} w[fa_u][i]\)This way you can query the\(fa_u\) The points within the connected blocks formed by the subtree are the same as the points within the\(u\) contribution. However, at this point a problem was identified, if\(u\) The distance of a point within a subtree from\(x\)fulfillment\(dis(u,x)\leq k\) both (... and...)\(dis(x,fa_u)\leq k-dis(u,fa_u)\), then the point is counted twice for contribution.

A simple idea is that every time the\(fa_u\) Subtract one time when searching at\(u\) The weights of the points within the subtree that satisfy both of these conditions.

Then we record\(w_0[u][i]\) because of\(dis(fa_u,v)=i\) both (... and...)\(v\) be part of\(u\) of the subtree.

Formalized.\(w_0[u][i]=\sum_{v\in } [dis(fa_u,v)==i]\times val[v]\)

exist\(fa_u\) Subtract\(\sum_{i=0}^{k-dis(u,fa_u)} w_0[u][i]\) That is, the completion of the\(fa_u\) The de-weighting.

The query range will now be\(u\) The connected block formed by the subtree expands to a\(fa_u\) The connected blocks are formed by constantly jumping upwards to the father, repeating the lookup and de-duplication operations, and eventually at most\(\log n\) After the second operation, we extend the query to the whole tree.

Modify a point\(u\) It is not difficult to realize that the inclusion of\(u\) The points of the weights are only in the\(u\) at the ancestor of the point-split tree, keep climbing up the point-split tree to change the\(w_0\) cap (a poem)\(w\) One modification can be accomplished.

Use dynamic open-point line segment trees or\(vector\) Tree arrays implemented in conjunction with the actual size of the connected blocks can reduce the single query prefix sum and modification to\(O(\log n)\) level, since at most the upward jump\(\log n\) Sub-ancestors, per query modification\(O(\log^2 n)\) The total complexity of the\(O(n\log^2 n+q\log^2 n)\)

#include<bits/stdc++.h>
using namespace std;

#define inf 1e8

const int maxn=1e5+5;

struct Edge
{
    int tot;
    int head[maxn];
    struct edgenode{int to,nxt;}edge[maxn*2];
    inline void add(int x,int y)
    {
        tot++;
        edge[tot].to=y;
        edge[tot].nxt=head[x];
        head[x]=tot;
    }
}T;//edge of the original tree
struct Tree//line segment tree
{
    int ct;
    int rt[maxn];
    struct node{int ch[2],val;}tree[maxn*55];
    void insert(int &p,int l,int r,int x,int y)
    {
        if(!p) p=++ct;
        if(l==r)
        {
            tree[p].val+=y;
            return ;
        }
        int mid=(l+r)>>1;
        if(x<=mid) insert(tree[p].ch[0],l,mid,x,y);
        else insert(tree[p].ch[1],mid+1,r,x,y);
        tree[p].val=tree[tree[p].ch[0]].val+tree[tree[p].ch[1]].val;
    }
    int query(int p,int l,int r,int ql,int qr)
    {
        if(ql>qr) return 0;
        if(!p) return 0;
        if(ql<=l&&r<=qr) return tree[p].val;
        if(l>qr||r<ql) return 0;
        int mid=(l+r)>>1;
        return query(tree[p].ch[0],l,mid,ql,qr)+query(tree[p].ch[1],mid+1,r,ql,qr);
    }
}w[2];//w[0] ibid. w_0,w[1] ibid. w

int n,tot,sum,m;
int val[maxn],siz[maxn],dis[maxn][25],fa[maxn],K[maxn];
//herein K recorded u At what level?,dis[u][i] because of u consultations with i Distance to ancestors of layers
//utilization O(1) lca maybe vector<pair> Records will get more beautifully realized
vector<int>E[maxn];
//Record edges in a point-split tree
bool cut[maxn],book[maxn];

inline void dfs_siz(int u)
{
    book[u]=true;siz[u]=1;
    for(int i=[u];i;i=[i].nxt)
    {
        int v=[i].to;
        if(book[v]||cut[v]) continue;
        dfs_siz(v);siz[u]+=siz[v];
    }
    book[u]=false;
}
inline int dfs_rt(int u,const int tot)
{
    book[u]=true;int ret=u;
    for(int i=[u];i;i=[i].nxt)
    {
        int v=[i].to;
        if(book[v]||cut[v]) continue;
        if(siz[v]*2>=tot){ret=dfs_rt(v,tot);break;}
    }
    book[u]=false;return ret;
}
inline void dfs_dis(int u,int k)//Find the first tree in the point-split tree k Layer Fathers and u proximity
{
    book[u]=true;
    for(int i=[u];i;i=[i].nxt)
    {
        int v=[i].to;
        if(cut[v]||book[v]) continue;
        dis[v][k]=dis[u][k]+1;
        dfs_dis(v,k);
    }
    book[u]=false;
}
inline void dfs_calc(int u,int op,int st,int k)//Add contribution to w[0] together with w[1]
{
    book[u]=true;
    w[op].insert(w[op].rt[st],0,inf,dis[u][k],val[u]);
    for(int i=[u];i;i=[i].nxt)
    {
        int v=[i].to;
        if(cut[v]||book[v]) continue;
        dfs_calc(v,op,st,k);
    }
    book[u]=false;
}

inline void dfs(int u,int f)
{
    dfs_siz(u);int g=dfs_rt(u,siz[u]);cut[g]=true;
    if(f) dfs_calc(g,0,g,K[f]);
    E[f].push_back(g);fa[g]=f;
    K[g]=K[fa[g]]+1;
    dfs_dis(g,K[g]);
    dfs_calc(g,1,g,K[g]);
    for(int i=[g];i;i=[i].nxt)
    {
        int v=[i].to;
        if(cut[v]) continue;
        dfs(v,g);
    }
}

void change(int u,int st,int now)//modifications
{
    if(!u) return ;
    w[1].insert(w[1].rt[u],0,inf,dis[st][K[u]],now-val[st]);//modifications w
    if(fa[u]) w[0].insert(w[0].rt[u],0,inf,dis[st][K[u]-1],now-val[st]);//modifications w[0]
    change(fa[u],st,now);//jump upwards
}
int dfs_ans(int u,int v,int st,int d)
{
    if(!u) return 0;
    if(d-dis[st][K[u]]<0) return dfs_ans(fa[u],u,st,d);//This distance exceeds the query range,jump upwards
    int res=w[1].query(w[1].rt[u],0,inf,0,d-dis[st][K[u]]);//consult (a document etc)
    if(v) res-=w[0].query(w[0].rt[v],0,inf,0,d-dis[st][K[u]]);//de-emphasize
    return res+dfs_ans(fa[u],u,st,d);//Jumping Fathers
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&val[i]);
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        (u,v);
        (v,u);
    }

    dfs(1,0);

    int ans=0;
    for(int i=1;i<=m;i++)
    {
        int op,x,y;
        scanf("%d%d%d",&op,&x,&y);
        x^=ans,y^=ans;
        if(op)
        {
            change(x,x,y);
            val[x]=y;
        }
        else
        {
            printf("%d\n",ans=dfs_ans(x,0,x,y));
        }
    }
}

wrap-up

Point-split trees deal with problems that are mostly with repair or forced online, and are usually associated with problems with\(\log\) The combination of data structures, while complexity is guaranteed, inevitably has a large constant.

If this question does not carry a fix, the query is taken offline to the point, using the plain prefix and (each time the size of the connected block shrinks) the\(\frac{2}{1}\)The length is also reduced.\(\frac{1}{2}\), prefixes and complexity are also similar to\(O(n\log n)\) (same level), it is possible to do\(O(n\log n+q\log n)\)

Example 3:ZJOI2007 Hide and Seek

Build a branching tree with points, each node is represented byset Store the distance to the black point within the subtree and add the distances of the two black points to find the longest black point distance through the node, globally using anset Maintain this information.

For a modification, in addition to changing each node'sset Also changes that will change the whole picture ofsetnotice from\(x\) Just jump up the point-split tree, similar to the modifications made during initialization.

time complexity\(O(n\log^2 n)\)

Example 4:P3345 ZJOI2015 Fantasy Land Strategy Game

This is a good question on point-split trees, which cleverly uses the properties of point-split trees and prototrees to facilitate a deeper understanding of tree partitioning.

An important technique is also used: bisection on a point-split tree to find keypoints.

Prove that the replenishment point is the weighted center of gravity of the tree, i.e., that the number of connected blocks divided by the deletion of this node does not have a point with weights andoutweighHalf of the total weighted sum.

Proof with greed: if the point\(u\) Not with a weighted center of gravity, then with the point\(u\) Connected connected blocks must have a weight and\(wsiz\geq \frac{wtot}{2}\)which\(wsiz\) is the sum of the weights of the connected blocks.\(wtot\) is the sum of the weights.

If the Chinden supply station is a point\(u\), it would be better to direct the replenishment station toward the weights and values greater than\(\frac{wtot}{2}\) The amount of change brought about by moving the connectivity block by one point is:\(val\times (wtot-wsiz)-val\times wsiz=val\times wtot-2\times val\times wsiz\), and by definition we have\(wsiz\times 2\geq wtot\)So.\(val\times wtot-2\times val\times wsiz\leq 0\)when and by when\(wsiz=\frac{wtot}{2}\) when the equal sign holds.

There may be more than one weighted center of gravity in the tree, but the answers to these weighted centers of gravity are the same, and multiple weighted centers of gravity can be found by referring to the case where the equals sign holds (they must be adjacent when they are).

In the original tree, we can start by pinning down a point as a refill station, and simply transfer the point where the refill station is located by the greedy transfer described above.

The time complexity of such a query is one\(O(n)\)

We need more efficient algorithms, and it's actually hard to think of tree partitioning due to the connection to the original tree form, but there are always some people with abnormal brain circuits.

--Bisection lookup of a tree by means of a point-split tree.

However, because of the difference in the morphology of the point-split tree and the original tree, a subtree in the point-split tree cannot represent a subtree in the original tree, so it is not possible to use edge transfers through a greedy strategy.

The solution is to consider a subtree as a connected block, centralize the information (weights and sums and points sums) of this connected block at the root of the subtree, and keep jumping the root of the subtree of the connected block required by the replenishment station by means of lookups, i.e., satisfy the\(wsiz\geq \frac{wtot}{2}\) The sub-tree roots of the connected block of the

It is easy to show that the points that satisfy such a requirement in the original tree must all satisfy this relation in the ancestors in the point-split tree.

When we get out of the\(f\) Jump to a subtree root\(u\) After further searching, those who may be in the point-split tree are\(u\) descendants and in the original tree is\(u\) ancestor nodes, the sum of the weights they represent should be the sum of the weights in the original tree in terms of\(u\) The information rooted at the root can be represented at this point in the\(f\) information for the root, so it is necessary to add the upper\(f\) The weights of the connected blocks are subtracted from the\(u\) The weights of the connected blocks are used to update these points. It is not hard to realize that all such points are not part of a\(u\) of a son subtree, we only need to update the root of this son subtree to ensure that the next step is correct.

Simply, put the connectivity block\(u\) together with\(f\) join\(i\) set up as\(u\) access points in the subtree of the sub-tree, each violent change in the\(i\) weights on the pointwise tree (and obviously change the weights of the ancestors on the pointwise tree as well), which ensures that for the\(u\) to say that the next step is the correct one. After jumping to the next node, the same operation is repeated, i.e., the next step can be guaranteed to be correct every time.

The algorithm above finds the location of the supply station, and the problem that follows is straightforward.

The points on each point-split tree store the sum of the weights within the subtree and the sum of the path multiplication weights, for which it is sufficient to do a single-point query to collect these values upwards and de-emphasize them.

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const int maxn=1e5+5;

struct Edge
{
    int tot;
    int head[maxn];
    struct edgenode{int to,nxt,val;}edge[maxn*2];
    inline void add(int x,int y,int z)
    {
        tot++;
        edge[tot].to=y;
        edge[tot].nxt=head[x];
        edge[tot].val=z;
        head[x]=tot;
    }
}T;

struct ed{int v;ll dis;};
vector<ed>fa[maxn],s[maxn];

int n,m,rt;
ll wtot;
int siz[maxn],itr[maxn];
bool cut[maxn],book[maxn];
ll dep[maxn],rel[maxn],wsiz[maxn];

inline int dfs1(int u)//Treating the original tree siz
{
    book[u]=true;
    for(int i=[u];i;i=[i].nxt)
    {
        int v=[i].to;
        if(!book[v]&&!cut[v]) siz[u]+=dfs1(v);
    }
    book[u]=false;return siz[u];
}
inline int fr(int u,const int &tot)//center of gravity
{
    book[u]=true;int ret=u;
    for(int i=[u];i;i=[i].nxt)
    {
        int v=[i].to;
        if(!book[v]&&!cut[v]&&2*siz[v]>=tot){ret=fr(v,tot);break;}
    }
    book[u]=false;return ret;
}
inline void dfs2(int u,const int &g)//Distance of preprocessing point-split trees
{
    book[u]=true;fa[u].push_back({g,dep[u]});
    for(int i=[u];i;i=[i].nxt)
    {
        int v=[i].to;
        if(book[v]||cut[v]) continue;
        dep[v]=dep[u]+[i].val;dfs2(v,g);
    }
    book[u]=false;siz[u]=1;return ;
}
inline void solve(int u,const int &f)//lit. construct points and divide the trees (idiom); fig. to subdivide a tree into parts
{
    dfs1(u);int g=fr(u,siz[u]);cut[g]=true;itr[g]=u;
    s[f].push_back({g,0}),fa[g].push_back({g,0});
    for(int i=[g];i;i=[i].nxt)
    {
        int v=[i].to;
        if(!cut[v]){dep[v]=[i].val;dfs2(v,g);solve(v,g);}
    }
    rt=g;
}
inline void modify(int u,int e)//Each modification operation,Modify the point weights on the tree
{
    wtot+=e;int p=u;rel[u]+=e;
    for(auto i:fa[u]) wsiz[]+=e;
    for(int i=fa[u].size()-1;i>=0;p=fa[u][i].v,i--)
    {
        for(auto &j:s[fa[u][i].v])if(==p){+=e*fa[u][i].dis;break;}
    }
}
inline void modi(int u,int e){for(auto i:fa[u]) wsiz[]+=e;}//Violent modification of point rights
inline int find(int u)//the two parts of a tree
{
    int ret=u;
    for(auto i:s[u])
    {
        if(wsiz[]*2>=wtot)
        {
            int del=wsiz[u]-wsiz[];
            modi(itr[],del);ret=find();modi(itr[],-del);
            break;
        }
    }
    return ret;
}
inline ll qry(int u)//consult (a document etc)
{
    ll ret=0;int p=u;
    for(int i=fa[u].size()-1;i>=0;p=fa[u][i].v,i--)
    {
        ret+=fa[u][i].dis*rel[fa[u][i].v];int f=fa[u][i].v;
        for(auto j:s[f]) if(!=p){ret+=fa[u][i].dis*wsiz[]+;}
    }
    return ret;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) siz[i]=1;
    for(int i=1;i<n;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        (x,y,z),(y,x,z);
    }
    solve(1,0);
    for(int i=1,u,e;i<=m;i++)
    {
        scanf("%d%d",&u,&e);
        modify(u,e);
        printf("%lld\n",qry(find(rt)));
    }
}

Part 4 De-duplication Problems in Point-Split Trees

  1. Maximum value query: A single maximum value query does not affect even the vast majority of cases where duplicates occur. For the problem of the number of maxima, stl or line tree can be used to maintain the maxima and their number from different partition centers.
  2. Path and query: generally subtract the excess of the summation in the father's query at the son of the point-split tree node.

Instead, enumerating the child nodes using point partitioning allows skipping from this step in some cases.

practice

P6626 Provincial Selection Joint Exam 2020 Paper B Messaging

P4178 Tree

P7215 JOISC2020 Capital City

P5311 Ynoi2011 Chengdu 7 Middle School

P2664 Tree games