[ZJOI2015]幻想乡战略游戏
题目描述
傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了。
在打仗之前,幽香现在面临一个非常基本的管理问题需要解决。 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来。
在游戏中,幽香可能在空地上增加或者减少一些军队。同时,幽香可以在一个空地上放置一个补给站。 如果补给站在点u上,并且空地v上有dv个单位的军队,那么幽香每天就要花费dv*dist(u,v)的金钱来补给这些军队。
由于幽香需要补给所有的军队,因此幽香总共就要花费为Sigma(Dv*dist(u,v),其中1<=V<=N)的代价。其中dist(u,v)表示u个v在树上的距离(唯一路径的权和)。
因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。
但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。
输入输出格式
输入格式:
第一行两个数n和Q分别表示树的点数和幽香操作的个数,其中点从1到n标号。 接下来n-1行,每行三个正整数a,b,c,表示a和b之间有一条边权为c的边。 接下来Q行,每行两个数u,e,表示幽香在点u上放了e单位个军队(如果e<0,就相当于是幽香在u上减少了|e|单位个军队,说白了就是du←du+e)。数据保证任何时刻每个点上的军队数量都是非负的。
输出格式:
对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。
输入输出样例
输入样例#1:
10 5
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
输出样例#1:
0
1
4
5
6
说明
对于所有数据,1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5 非常神奇的是,对于所有数据,这棵树上的点的度数都不超过20,且N,Q>=1
题解
统计点对间信息还带修改那显然是点分树
先对原树暴力构建点分树
顺便从根向儿子连新边
套路的用\(v1[u]\)表示u的子树对u的贡献
\(v2[u]\)表示u的子树对\(fa[u]\)的贡献
然后由于是点权,所以还要统计子树的点权信息
\(Sum[u]\)表示u的子树内所有点权和
这样就可以对一个点快速修改并计算答案了
然后考虑怎么找最小的点
可以考虑在点分树上查找
一开始在root处
然后枚举ta所有的原来的边
如果移动到其他点的答案比原答案小,那么就继续走到他的这个儿子所对应的下一级根节点
直到所有的点都不如ta优就返回答案
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
# define int long long
const int M = 100005 ;
const int INF = 1e18 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
bool vis[M] ;
int n , num , hea[M] ;
int st[M][20] , dis[M][20] , dep[M] ;
int tot , tmin = INF , root , size[M] ;
int fa[M] , prep ;
int v1[M] , v2[M] , Sum[M] , Ans ;
struct P { int v , Nrt ; } ;
struct E { int Nxt , to , dis ; } edge[M << 1] ;
vector < P > vec[M] ;
inline void add_edge(int from , int to , int dis) {
edge[++num].Nxt = hea[from] ; edge[num].to = to ;
edge[num].dis = dis ; hea[from] = num ;
}
void FirDfs(int u , int father) {
dep[u] = dep[father] + 1 ; st[u][0] = father ;
for(int i = hea[u] ; i ; i = edge[i].Nxt) {
int v = edge[i].to ; if(v == father) continue ;
dis[v][0] = edge[i].dis ; FirDfs(v , u) ;
}
}
inline void ST() {
for(int j = 1 ; j <= 17 ; j ++)
for(int i = 1 ; i <= n ; i ++) {
st[i][j] = st[st[i][j - 1]][j - 1] ;
dis[i][j] = dis[i][j - 1] + dis[st[i][j - 1]][j - 1] ;
}
}
inline int Gdis(int u , int v) {
int ret = 0 ; if(dep[u] < dep[v]) swap(u , v) ;
for(int i = 17 ; i >= 0 ; i--)
if(dep[st[u][i]] >= dep[v]) {
ret += dis[u][i] ;
u = st[u][i] ;
}
if(u == v) return ret ;
for(int i = 17 ; i >= 0 ; i --)
if(st[u][i] != st[v][i]) {
ret += dis[u][i] + dis[v][i] ;
u = st[u][i] , v = st[v][i] ;
}
return ret + dis[u][0] + dis[v][0] ;
}
void Getroot(int u , int father) {
size[u] = 1 ; int Mx = -1 ;
for(int i = hea[u] ; i ; i = edge[i].Nxt) {
int v = edge[i].to ; if(v == father || vis[v]) continue ;
Getroot(v , u) ; size[u] += size[v] ; Mx = max(Mx , size[v]) ;
}
Mx = max(Mx , tot - size[u]) ; if(Mx < tmin) tmin = Mx , root = u ;
}
void Build(int u , int father) {
fa[u] = father ; vis[u] = true ;
for(int i = hea[u] ; i ; i = edge[i].Nxt) {
int v = edge[i].to ; if(vis[v]) continue ;
tot = size[v] ; tmin = INF ;
Getroot(v , u) ;
vec[u].push_back((P) { v , root }) ;
Build(root , u) ;
}
}
inline void Update(int u , int v) {
Sum[u] += v ;
for(int i = u , dis1 ; fa[i] ; i = fa[i]) {
dis1 = Gdis(u , fa[i]) ;
v1[fa[i]] += v * dis1 , v2[i] += v * dis1 ;
Sum[fa[i]] += v ;
}
}
inline int Calc(int u) {
int ret = v1[u] ;
for(int x = u , dis1 ; fa[x] ; x = fa[x]) {
dis1 = Gdis(u , fa[x]) ;
ret += v1[fa[x]] - v2[x] ;
ret += (Sum[fa[x]] - Sum[x]) * dis1 ;
}
return ret ;
}
int Dfs(int u) {
int Ans = Calc(u) , temp ;
for(int i = 0 ; i < vec[u].size() ; i ++) {
temp = Calc(vec[u][i].v) ;
if(temp < Ans) return Dfs(vec[u][i].Nrt) ;
}
return Ans ;
}
# undef int
int main() {
# define int long long
n = read() ; int Q = read() ;
for(int i = 1 , u , v , w ; i < n ; i ++) {
u = read() , v = read() , w = read() ;
add_edge(u , v , w) ; add_edge(v , u , w) ;
}
FirDfs(1 , 0) ; ST() ;
tot = n ; Getroot(1 , 0) ;
prep = root ; Build(root , 0) ;
int u , v ;
while(Q --) {
u = read() , v = read() ;
Update(u , v) ;
printf("%lld\n",Dfs(prep)) ;
}
return 0 ;
}