[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
",Dfs(prep)) ;
    }
    return 0 ;
}
原文地址:https://www.cnblogs.com/beretty/p/10064992.html