显然,一棵树的带权重心最多只有两个,最少会有一个,而且在这两个点的答案一定相等。(都是带权重心当然相等)鉴于点分治的写法貌似并不太需要这样的分析,就不说了。我不会
首先建出一颗点分树,然后考虑在点分树上跳儿子来保证复杂度,于是我们就需要快速算出所有点到这个重心的带权距离和。由于这题是点分树,考虑在点分树上维护一些信息来快速计算这个东西。
对于我们当前枚举到的这个点\(x\),一部分答案贡献来自点分树上\(x\)的子树以内,这部分可以直接记录一个\(dsum[x]\)表示在点分树上\(x\)的子树内的所有点到\(x\)的距离和。另一部分来自子树外,我们先只考虑\(x\)在点分树上的父节点\(fa[x]\)的子树里的点对这里的贡献,至于\(fa[fa[x]]\)可以以此类推。那么这个贡献就又分为两部分,一部分是\(fa[x]\)的子树内根不为\(x\)的子树内的点到\(fa[x]\)的距离。这个时候显然如果直接使用\(dsum[fa[x]]\)会导致重复计算,所以要记录一个\(fsum[x]\)来表示\(x\)这个子树内的所有点到\(fa[x]\)的距离和。那么这一部分的答案贡献就是\(dsum[fa[x]]-fsum[x]\)。那么还有一部分的答案就是这些点都到了\(fa[x]\)之后再到\(x\)的距离和,那这部分的答案就是\(dis(fa[x],x)\times(ssum[fa[x]]-ssum[x])\)。这里的\(ssum[x]\)就是\(x\)子树内的\(size\)之和。\(dis\)的处理也需要树剖来维护。然后向上爬点分树的时候注意一下,哪些地方应该用原始的\(x\),哪些地方应该用向上跳了的结点就可以了。具体的实现在代码里。时间复杂度是\(O(nlogn^3)\),由于树剖常数小,可以跑过。
然后我写的时候有一个想法,在于为什么不能直接维护一个点和根的关系呢?这样不就\(O(1)\)了吗?后来想想发现,我们的算法是基于外部的点要到\(x\)必须要经过\(x\)在点分树上的祖先才能够做到枚举的。这点要想通我个人认为还是很重要的。
upd:一个要注意的一点是在求答案的时候要在原树上找儿子,在点分树上跳重心。
#include <cstdio>
using namespace std;
#define R register
#define LL long long
const int MAXN=5e5+10;
inline int read() {
int x=0,f=1; char a=getchar();
for(;a>'9'||a<'0';a=getchar()) if(a=='-') f=-1;
for(;a>='0'&&a<='9';a=getchar()) x=x*10+a-'0';
return x*f;
}
inline int max(int x,int y) { return x > y ? x : y; }
inline int min(int x,int y) { return x < y ? x : y; }
int n,q;
namespace Graph {
int cnt,head[MAXN];
struct edge { int to,w,next; } e[MAXN<<1];
inline void add(int x,int y,int w) {
e[++cnt]={y,w,head[x]}; head[x]=cnt;
}
int dep[MAXN],dis[MAXN],fa[MAXN],son[MAXN],siz[MAXN],top[MAXN];
inline void dfs1(int x,int fx) {
fa[x]=fx; dep[x]=dep[fx]+1; siz[x]=1;
for(R int i=head[x];i;i=e[i].next) {
int y=e[i].to; if(y==fx) continue;
dis[y]=dis[x]+e[i].w; dfs1(y,x); siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
inline void dfs2(int x,int topx) {
top[x]=topx;
if(son[x]) dfs2(son[x],topx);
for(R int i=head[x];i;i=e[i].next) {
int y=e[i].to;
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
inline int Lca(int x,int y) {
while(top[x]!=top[y])
if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];
else y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
inline int Dist(int x,int y) {
return dis[x]+dis[y]-dis[Lca(x,y)]*2;
}
inline void Build() {
dfs1(1,0); dfs2(1,1);
}
}
namespace Tree {
int Cnt,Head[MAXN];
struct Edge { int to,rt,next; } E[MAXN<<1];
inline void Add(int x,int y,int rt) {
E[++Cnt]={ y, rt, Head[x] }; Head[x]=Cnt;
}
int RT,Root,mx[MAXN],vis[MAXN],siz[MAXN],total,fa[MAXN];
inline void findrt(int x,int fx) {
mx[x]=0; siz[x]=1;
for(R int i=Graph::head[x];i;i=Graph::e[i].next) {
int y=Graph::e[i].to;
if(y==fx||vis[y]) continue;
findrt(y,x); siz[x]+=siz[y];
mx[x]=max(mx[x],siz[y]);
}
mx[x]=max(mx[x],total-siz[x]);
if(mx[x]<mx[Root]) Root=x;
}
inline void build(int x,int fx) {
fa[x]=fx; vis[x]=1;
for(R int i=Graph::head[x];i;i=Graph::e[i].next) {
int y=Graph::e[i].to; if(vis[y]) continue;
total=siz[y]; Root=0;
findrt(y,0); Add(x,y,Root);
build(Root,x);
}
}
inline void Build() {
mx[0]=0x7f7f7f7f;
Root=0; findrt(1,0);findrt(Root,0); RT=Root; build(Root,0);
}
LL sums[MAXN],su***XN],sumf[MAXN];
inline void Modify(int x,int k) {
sums[x]+=k;
for(R int i=x;fa[i];i=fa[i]) {
int len=Graph::Dist(x,fa[i]);
sums[fa[i]]+=k;
sumd[fa[i]]+=1LL*k*len;
sumf[i]+=1LL*k*len;
}
}
inline LL Count(int x) {
LL res=sumd[x];
for(R int i=x;fa[i];i=fa[i]) {
int len=Graph::Dist(x,fa[i]);
res+=(sums[fa[i]]-sums[i])*len;
res+=sumd[fa[i]]-sumf[i];
}
return res;
}
inline LL Ask(int x) {
LL res=Count(x);
for(R int i=Head[x];i;i=E[i].next) {
int y=E[i].to;
if(Count(y)<res) return Ask(E[i].rt);
}
return res;
}
inline LL Ask() { return Ask(RT); }
}
int main() {
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
n=read(); q=read();
for(R int i=1;i<n;i++) {
int x=read(),y=read(),z=read();
Graph::add(x,y,z); Graph::add(y,x,z);
}
Graph::Build(); Tree::Build();
while(q--) {
int x=read(),y=read();
Tree::Modify(x,y);
printf("%lld\n",Tree::Ask());
}
return 0;
}