Ark_
2019-03-20 16:20:03
树的结构不变,每个点有点权,每一条边有边权,有修改点权的操作,设
我们把补给站叫做决策点,那么假设当前最优决策点为
所以说
也就是
不难发现,满足这个式子的儿子至多有一个.那么一个暴力的想法就是每次询问从根开始,往儿子下面走,如果不存在儿子比自己更优,那么自己就是决策点,否则就进入比自己优的儿子的子树.
这样每次询问的时间复杂度看似是
我们将树的深度减小到了
再考虑修改,只用把自己在点分树中的祖先修改就行了,每一次时间复杂度
实际上,每一次计算/修改都需要算两个点在原树上的距离,要求
修改/计算具体看代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
template<typename T>inline void read(T &num) {
char ch; int flg = 1;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')flg=-flg;
for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar());
num*=flg;
}
const int MAXN = 100005;
int n, q, fir[MAXN], cnt;
struct edge { int to, nxt, w; }e[MAXN<<1];
inline void add(int u, int v, int wt) {
e[cnt] = (edge){ v, fir[u], wt }, fir[u] = cnt++;
e[cnt] = (edge){ u, fir[v], wt }, fir[v] = cnt++;
}
int dis[MAXN], son[MAXN], sz[MAXN], top[MAXN], fa[MAXN], dep[MAXN];
void dfs(int u, int ff) {
dep[u] = dep[fa[u]=ff] + (sz[u]=1);
for(int i = fir[u], v; ~i; i = e[i].nxt)
if((v=e[i].to) != ff) {
dis[v] = dis[u] + e[i].w;
dfs(v, u), sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp;
if(son[u]) dfs2(son[u], tp);
for(int i = fir[u], v; ~i; i = e[i].nxt)
if((v=e[i].to) != fa[u] && v != son[u])
dfs2(v, v);
}
int lca(int u, int v) { //
while(top[u] != top[v]) {
if(dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
int dist(int u, int v) { return dis[u] + dis[v] - 2*dis[lca(u,v)]; }
int Fa[MAXN], size[MAXN], Size, Minr, root, info[MAXN], CNT;
struct EDGE { int to, nxt, rt; }E[MAXN];
inline void ADD(int u, int v, int rr) {
E[CNT] = (EDGE){ v, info[u], rr }, info[u] = CNT++;
}
bool vis[MAXN];
void Getrt(int u, int ff) {
size[u] = 1;
int ret = 0;
for(int i = fir[u], v; ~i; i = e[i].nxt)
if((v=e[i].to) != ff && !vis[v]) {
Getrt(v, u), size[u] += size[v];
ret = max(ret, size[v]);
}
ret = max(ret, Size-size[u]);
if(ret < Minr) Minr = ret, root = u;
}
void DFS(int u, int ff) {
vis[u] = 1; Fa[u] = ff;
for(int i = fir[u], v; ~i; i = e[i].nxt)
if(!vis[v=e[i].to]) {
Minr = n; Size = size[v];
Getrt(v, u);
ADD(u, v, root);
DFS(root, u);
}
}
LL sum[MAXN]; //子树点数之和
LL sumd[MAXN]; //子树内的距离之和
LL sumf[MAXN]; //子树对父亲的贡献
inline void Modify(int u, int val) {
sum[u] += val;
for(int i = u; Fa[i]; i = Fa[i]) {
int len = dist(u, Fa[i]);
sum[Fa[i]] += val;
sumd[Fa[i]] += 1ll * val * len;
sumf[i] += 1ll * val * len;
}
}
inline LL Count(int u) { //多理解一会,画画图多YY下
LL res = sumd[u]; //自己子树的距离之和
for(int i = u; Fa[i]; i = Fa[i]) {
int len = dist(u, Fa[i]);
res += (sum[Fa[i]]-sum[i]) * len; //Fa[i]的其他子树额外的点数 * 这一条边
res += (sumd[Fa[i]]-sumf[i]); //Fa[i]的其他子树的点到Fa[i]的距离之和
//上面两个统计都必须要减去这个子树的贡献,否则算重了
}
return res;
}
LL Query(int u) {
LL tmp = Count(u);
for(int i = info[u]; ~i; i = E[i].nxt)
if(Count(E[i].to) < tmp) return Query(E[i].rt); //儿子比自己优就去对应的重心
return tmp;
}
int main () {
memset(fir, -1, sizeof fir);
memset(info, -1, sizeof info);
read(n), read(q);
for(int i = 1, x, y, z; i < n; ++i)
read(x), read(y), read(z), add(x, y, z);
dfs(1, 0), dfs2(1, 1);
Size = Minr = n; Getrt(1, 0); //先求一次重心
int RT = root; //作为根
DFS(root, 0);
int x, y;
while(q--) {
read(x), read(y);
Modify(x, y);
printf("%lld\n", Query(RT));
}
}