problem
luogu-P3345
solution
这是一个带权重心的题,考察动态点分治。点分治?呵,不可能的,这辈子都不可能写点分治
我们重新考虑重心的性质:以这个点为根时,所有子树的大小不会超过整体大小的一半。
带权重心无非是树上每个点不再是等权的 111,而是 did_idi。
考虑随机从一个点走向重心:当前假设重心为 xxx,我们维护一个值 sizxsiz_xsizx 表示 xxx 所在子树所有节点的权值和。
那么如果存在一个 xxx 的子节点 yyy,满足 2∗sizy>2*siz_y>2∗sizy> xxx 树中所有点的权值和,则 yyy 更优,否则 xxx 更优。
比较暴力的想法就是从根节点一路往下找,但这样每次 O(n)O(n)O(n) 搜一遍树时间开销过大,这是因为走了很多没有用的点。
因此我们考虑树链剖分,这样可以获得一个 dfs\text{dfs}dfs 序,然后用线段树维护区间 sizsizsiz 的最大值。
树链剖分得到的 dfs\text{dfs}dfs 序中深度深的节点 dfs\text{dfs}dfs 序更大。
查询时就在线段树上二分,每次分为 [l,mid],(mid,r][l,mid],(mid,r][l,mid],(mid,r],只要 (mid,r](mid,r](mid,r] 区间的最大值满足重心判断就往右子树走,否则走左子树。
时间复杂度 O(logn)O(\log n)O(logn)。
现在假设已经求出了重心 xxx,接下来考虑如何计算答案。
ans=∑idi∗dis(x,i)=∑idi∗(depx+depi−2∗deplca(x,i))=depx∑di+∑di∗depi−2∑di∗deplca(x,i)ans=\sum_id_i*dis(x,i)=\sum_id_i*(dep_x+dep_i-2*dep_{lca(x,i)}) \\=dep_x\sum d_i+\sum d_i*dep_i-2\sum d_i*dep_{lca(x,i)} ans=i∑di∗dis(x,i)=i∑di∗(depx+depi−2∗deplca(x,i))=depx∑di+∑di∗depi−2∑di∗deplca(x,i)
∑di,∑di∗depi\sum d_i,\sum d_i*dep_i∑di,∑di∗depi 直接用两个变量维护即可,问题在于如何维护 ∑di∗deplca(x,i)\sum d_i*dep_{lca(x,i)}∑di∗deplca(x,i)。
其实这是一个很经典的树链剖分维护形式。
当 di←+wd_i\leftarrow +wdi←+w 时,把 iii 到根路径上所有点均 +w+w+w,然后查询 xxx 到根路径上的点权值和。
先树差分一下,即在线段树中 iii 存储 depi−depfaidep_i-dep_{fa_i}depi−depfai。这样从 iii 一路加到根上面算出的才是 iii 这个点本身的实际贡献。
很好写,时间复杂度 O(nlog2n)O(n\log ^2n)O(nlog2n) 也能跑过。
code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 100005
int n, Q;
vector < pair < int, int > > G[maxn];
int f[maxn], dep[maxn], dfn[maxn], id[maxn];namespace SGT {struct node { int Max, sum, val, tag; }t[maxn << 2];#define lson now << 1#define rson now << 1 | 1#define mid (l + r >> 1)void build( int now, int l, int r ) {if( l == r ) { t[now].val = dep[id[l]] - dep[f[id[l]]]; return; }build( lson, l, mid );build( rson, mid + 1, r );t[now].val = t[lson].val + t[rson].val;}void pushdown( int now ) {if( ! t[now].tag ) return;t[lson].tag += t[now].tag;t[lson].Max += t[now].tag;t[lson].sum += t[lson].val * t[now].tag;t[rson].tag += t[now].tag;t[rson].Max += t[now].tag;t[rson].sum += t[rson].val * t[now].tag;t[now].tag = 0;}void modify( int now, int l, int r, int L, int R, int x ) {if( R < l or r < L ) return;if( L <= l and r <= R ) { t[now].tag += x;t[now].Max += x;t[now].sum += t[now].val * x;return;}pushdown( now );modify( lson, l, mid, L, R, x );modify( rson, mid + 1, r, L, R, x );t[now].Max = max( t[lson].Max, t[rson].Max );t[now].sum = t[lson].sum + t[rson].sum;}int query( int now, int l, int r, int L, int R ) {if( R < l or r < L ) return 0;if( L <= l and r <= R ) return t[now].sum;pushdown( now );return query( lson, l, mid, L, R ) + query( rson, mid + 1, r, L, R );}int query( int x ) {int now = 1, l = 1, r = n;while( l ^ r ) {pushdown( now );if( t[rson].Max >= x ) now = rson, l = mid + 1;else now = lson, r = mid;}return id[l];}
}namespace Qtree {int cnt = 0;int son[maxn], siz[maxn], top[maxn];void dfs1( int u, int fa ) {f[u] = fa; siz[u] = 1;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first, w = G[u][i].second;if( v == fa ) continue;else dep[v] = dep[u] + w, dfs1( v, u );siz[u] += siz[v];if( siz[v] > siz[son[u]] ) son[u] = v;}}void dfs2( int u, int t ) {top[u] = t, id[dfn[u] = ++ cnt] = u;if( son[u] ) dfs2( son[u], t );else return;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first;if( v == f[u] or v == son[u] ) continue;else dfs2( v, v );}}void modify( int x, int y ) {while( top[x] ) {SGT :: modify( 1, 1, n, dfn[top[x]], dfn[x], y );x = f[top[x]];}}int query( int x ) {int ans = 0;while( top[x] ) {ans += SGT :: query( 1, 1, n, dfn[top[x]], dfn[x] );x = f[top[x]];}return ans;}
}signed main() {scanf( "%lld %lld", &n, &Q );for( int i = 1, u, v, w;i < n;i ++ ) {scanf( "%lld %lld %lld", &u, &v, &w );G[u].push_back( make_pair( v, w ) );G[v].push_back( make_pair( u, w ) );}Qtree :: dfs1( 1, 0 );Qtree :: dfs2( 1, 1 );SGT :: build( 1, 1, n );int x, y, sum1 = 0, sum2 = 0;while( Q -- ) {scanf( "%lld %lld", &x, &y );Qtree :: modify( x, y );sum1 += dep[x] * y;sum2 += y;int p = SGT :: query( sum2 + 1 >> 1 );printf( "%lld\n", sum1 + dep[p] * sum2 - (Qtree :: query( p ) << 1) );}return 0;
}