细节挺多的,但是也确实加深了我对动态点分治的理解.
这段代码值得关注:
ll query(int u)
{
ll re=calc(u);
for(int i=hd[u];i;i=nex[i])
{
ll t=calc(to[i]);
if(t<re) return query(hto[i]);
}
return re;
}
点分树的结构是和原树不同的.
我们知道,最优决策点和根节点的连线上的点的答案一定是越来越优的.
而如果发现 $to[i]$ 所在子树中,$to[i]$ 更优,那么想在点分树中到达 $to[i]$,直接走到 $to[i]$所在重心即可.
开始的时候思维太僵化了,一直想的是只靠重心之间转移,但其实我们也要将点分树和原树结合起来.
#include <cstdio>
#include <vector>
#include <algorithm>
#define N 100004
#define inf 1000000000000000
#define ll long long
#define setIO(s) freopen(s".in","r",stdin) , freopen(s".out","w",stdout)
using namespace std;
int edges,n;
int hd[N],to[N<<1],nex[N<<1],val[N<<1], hto[N<<1];
void add(int u,int v,int c)
{
nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c;
}
namespace tree
{
ll dis[N];
int fa[N],top[N],size[N],son[N],dep[N];
void dfs1(int u,int ff)
{
fa[u]=ff,size[u]=1;
for(int i=hd[u];i;i=nex[i])
if(to[i]!=ff)
{
int v=to[i];
dep[v]=dep[u]+1,dis[v]=dis[u]+1ll*val[i];
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;
if(son[u]) dfs2(son[u],tp);
for(int i=hd[u];i;i=nex[i])
if(to[i]!=fa[u]&&to[i]!=son[u])
dfs2(to[i],to[i]);
}
int LCA(int x,int y)
{
while(top[x]!=top[y])
dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
ll Dis(int x,int y)
{
return dis[x]+dis[y]-2ll*dis[LCA(x,y)];
}
};
vector<int>e[N];
int root,sn;
int Fa[N],mx[N],size[N],vis[N];
ll F[N],G[N],num[N],tmp;
void getroot(int u,int ff)
{
size[u]=1,mx[u]=0;
for(int i=hd[u];i;i=nex[i])
if(to[i]!=ff&&!vis[to[i]])
getroot(to[i],u),size[u]+=size[to[i]],mx[u]=max(mx[u],size[to[i]]);
mx[u]=max(mx[u],sn-size[u]);
if(mx[u]<mx[root]) root=u;
}
void dfs(int u,int ff)
{
size[u]=1;
for(int i=hd[u];i;i=nex[i])
if(to[i]!=ff&&!vis[to[i]])
dfs(to[i],u),size[u]+=size[to[i]];
}
void prepare(int u)
{
vis[u]=1;
for(int i=hd[u];i;i=nex[i])
if(!vis[to[i]])
{
dfs(to[i],u),root=0,sn=size[to[i]],getroot(to[i],u),hto[i]=root,Fa[root]=u,prepare(root);
}
}
void Update(int u,ll v)
{
int U=u;
num[u]+=v;
for(;Fa[u];u=Fa[u])
{
F[Fa[u]]+=v*tree::Dis(U,Fa[u]);
G[u]+=v*tree::Dis(U,Fa[u]);
num[Fa[u]]+=v;
}
}
ll calc(int u)
{
ll re=F[u];
int U=u;
for(;Fa[u];u=Fa[u])
{
re+=F[Fa[u]]-G[u]+1ll*(num[Fa[u]]-num[u])*tree::Dis(U,Fa[u]);
}
return re;
}
ll query(int u)
{
ll re=calc(u);
for(int i=hd[u];i;i=nex[i])
{
ll t=calc(to[i]);
if(t<re) return query(hto[i]);
}
return re;
}
int main()
{
int i,j,Q;
// setIO("input");
scanf("%d%d",&n,&Q);
for(i=1;i<n;++i)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c),add(a,b,c),add(b,a,c);
}
int cc=0;
tree::dfs1(1,0);
tree::dfs2(1,1);
sn=n,mx[0]=n,root=0,getroot(1,0),cc=root,prepare(root);
for(int cas=1;cas<=Q;++cas)
{
int u,e;
scanf("%d%d",&u,&e), Update(u,1ll*e);
printf("%lld\n",query(cc));
}
return 0;
}