Description
傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了。 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决。 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来。在游戏中,幽香可能在空地上增加或者减少一些军队。同时,幽香可以在一个空地上放置一个补给站。 如果补给站在点u上,并且空地v上有dv个单位的军队,那么幽香每天就要花费dv×dist(u,v)的金钱来补给这些军队。由于幽香需要补给所有的军队,因此幽香总共就要花费为Sigma(Dv*dist(u,v),其中1<=V<=N)的代价。其中dist(u,v)表示u个v在树上的距离(唯一路径的权和)。 因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。
Input
Output
对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。
Sample Input
1 2 1
2 3 1
2 4 1
1 5 1
2 6 1
2 7 1
5 8 1
7 9 1
1 10 1
3 1
2 1
8 1
3 1
4 1
Sample Output
1
4
5
6
分析:
题意:维护带修改的带权重心到其余点的带权距离和
怎么求出带权重心呢?
考虑一个点
x
x
,我们维护一个值表示
x
x
所在子树所有结点的权值和
那么如果存在一个点,
y
y
是的一个子结点,使得
f[y]∗2>
f
[
y
]
∗
2
>
树中所有点的权值和
那么
y
y
一定比更优,反之
x
x
比更优
那么一种比较朴素的想法就是,从根结点开始一路向下找
然而这样每次会遍历一些没有用的结点
因此我们考虑树链剖分,这样就可以得到一个dfs序
然后用线段树维护某一个区间
f
f
的最大值
因为链剖得到的dfs序中深度较深的节点在较靠后的位置
查询时就可以用二分的思想:每次将对应的区间分成
只要
[mid+1,r]
[
m
i
d
+
1
,
r
]
区间的最大值满足条件就跳转到
[mid+1,r]
[
m
i
d
+
1
,
r
]
,直到找到一个点为止
第二问:
dis
d
i
s
:点
i
i
到根的距离
:补给站选取的位置
sum[i]
s
u
m
[
i
]
:表示
i
i
子树中军队的个数
答案分成三部分,第二部分可以直接计算求解
第一部分用线段树维护区间和,每次修改的时候只需要对于需要修改的点进行单点修改
关键是第三部分如何求解
每个点只会有一个重儿子,而每次查询的时候走的都是重链
考虑
lca(loc,i)
l
c
a
(
l
o
c
,
i
)
的取值,只可能是
loc
l
o
c
到根路径上的点
如果我们能计算出每个点的贡献,那么问题就迎刃而解了
我们对于每个点,维护该点子树中的点(除去重儿子子树中的点)的
sum[i]∗dis
s
u
m
[
i
]
∗
d
i
s
的值
如果
loc
l
o
c
在重儿子的子树中,该点记录的这个值就是以该点为lca的贡献值
因为我们在向上走的过程中需要从一个重链跳到另一个重链,而走的这条边是轻边,这个特殊点需要单独计算
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
const int N=100005;
int n,m,st[N],tot;
int size[N<<2],son[N],deep[N],pre[N],top[N],num[N],shu[N],clo=0,Sum;
ll dis[N];
struct node{
int y,nxt,v;
};
node way[N<<1];
struct Tree{
ll tr,val,sum,dis,ad;
};
Tree t[N<<2];
void add(int u,int w,int z)
{
tot++;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
tot++;way[tot].y=u;way[tot].v=z;way[tot].nxt=st[w];st[w]=tot;
}
void dfs_1(int now,int fa,int dep)
{
deep[now]=dep;size[now]=1;pre[now]=fa;
int maxx=0;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa)
{
dis[way[i].y]=dis[now]+(ll)way[i].v;
dfs_1(way[i].y,now,dep+1);
size[now]+=size[way[i].y];
if (size[way[i].y]>maxx)
maxx=size[way[i].y],son[now]=way[i].y;
}
}
void dfs_2(int now,int fa)
{
if (son[fa]!=now) top[now]=now;
else top[now]=top[fa];
num[now]=++clo; shu[clo]=now;
if (son[now])
{
dfs_2(son[now],now);
for (int i=st[now];i;i=way[i].y)
if (way[i].y!=fa&&way[i].y!=son[now])
dfs_2(way[i].y,now);
}
}
void update(int now)
{
int lc=now<<1;
int rc=now<<1|1;
t[now].sum=max(t[lc].sum,t[rc].sum);
t[now].val=t[lc].val+t[rc].val;
t[now].tr=t[lc].val+t[rc].val;
t[now].dis=t[lc].dis+t[rc].dis;
}
void build(int bh,int l,int r)
{
if (l==r)
{
t[bh].val=0; t[bh].dis=dis[shu[l]];
t[bh].sum=0; t[bh].tr=0;
//val表示lca为该点,轻边相连的子树与重儿子之间对答案的贡献
}
int mid=(l+r)>>1;
build(bh<<1,l,mid);
build(bh<<1|1,mid+1,r);
update(bh);
}
void push(int now)
{
if (t[now].ad)
{
t[now<<1].ad+=t[now].ad; t[now<<1|1].ad+=t[now].ad;
t[now<<1].sum+=t[now].ad; t[now<<1|1].sum+=t[now].ad;
t[now].ad=0;
}
}
void addsize(int now,int l,int r,int L,int R,int v)
{
if (l>=L&&r<=R)
{
t[now].ad+=(ll)v;
t[now].sum+=(ll)v;
return;
}
push(now);
int mid=(l+r)>>1;
if (L<=mid) addsize(now<<1,l,mid,L,R,v);
if (R>mid) addsize(now<<1|1,mid+1,r,L,R,v);
update(now);
}
void change(int bh,int l,int r,int x,ll v,int ff)
{
if (l==r){
if (!ff) t[bh].val+=v;
else t[bh].tr=v;
return;
}
push(bh);
int mid=(l+r)>>1;
if (x<=mid) change(bh<<1,l,mid,x,v,ff);
else change(bh<<1|1,mid+1,r,x,v,ff);
update(bh);
}
void solve(int x,int y,int z)
{
change(1,1,n,num[x],(ll)size[y]*dis[y],1);
change(1,1,n,num[y],(ll)z*dis[y],0);
int f1=top[x],f2=top[y];
while (f1!=f2)
{
addsize(1,1,n,num[f2],num[y],z);
change(1,1,n,num[pre[f2]],(ll)z*dis[pre[f2]],0);
y=pre[f2]; f2=top[y];
}
addsize(1,1,n,num[x],num[y],z);
}
int find(int now,int l,int r)
{
if (l==r) return l;
push(now);
int mid=(l+r)>>1;
if (t[now<<1|1].sum*2>=Sum) return find(now<<1|1,mid+1,r);
else return find(now<<1,l,mid);
}
ll ask(int now,int l,int r,int L,int R)
{
if (l>=L&&r<=R) return t[now].val;
int mid=(l+r)>>1;
push(now);
ll ans=0;
if (L<=mid) ans+=ask(now<<1,l,mid,L,R);
if (R>mid) ans+=ask(now<<1|1,mid+1,r,L,R);
return ans;
}
Tree findpo(int now,int l,int r,int x)
{
if (l==r) return t[now];
push(now);
int mid=(l+r)>>1;
if (x<=mid) return findpo(now<<1,l,mid,x);
else return findpo(now<<1|1,mid+1,r,x);
}
ll solve_(int x,int y)
{
ll ans=0;
Tree a=findpo(1,1,n,num[y]);
ans-=a.val;
int f1=top[x],f2=top[y];
while (f1!=f2)
{
ans+=ask(1,1,n,num[f2],num[y]);
y=pre[f2];
Tree a=findpo(1,1,n,num[y]);
Tree b=findpo(1,1,n,num[f2]);
ans=ans-a.val+dis[y]*(a.sum-b.sum);
f2=top[y];
}
ans+=ask(1,1,n,num[x],num[y]);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++)
{
int u,w,z;
scanf("%d%d%d",&u,&w,&z);
add(u,w,z);
}
dfs_1(1,0,1);
dfs_2(1,0);
memset(size,0,sizeof(size)); Sum=0;
build(1,1,n);
for (int i=1;i<=m;i++)
{
int x,z;
scanf("%d%d",&x,&z);
Sum+=z; size[x]+=z;
solve(1,x,z);
int p=find(1,1,n); p=shu[p];
ll ans=0;
ans+=(ll)(dis[p]*Sum); ans+=t[1].tr;
Tree a=findpo(1,1,n,num[p]);
ans-=(ll)2*solve_(1,p); ans-=(ll)2*dis[p]*a.sum;
printf("%lld\n",ans);
}
}