• [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 ;
    }
    
  • 相关阅读:
    win10快捷键
    emmet语法
    sublime 快捷键,左菜单乱码
    Windows 10 下 MarkdownPad2 预览无法显示是怎么回事?
    html5上传图片
    mysql多表查询
    用C#创建一个窗体,在构造函数里面写代码和在from_load事件里面写代码有什么不同?
    有复选框情况下,sql拼写技巧
    C# 图片 旋转和翻转 RotateFlip
    ListView
  • 原文地址:https://www.cnblogs.com/beretty/p/10064992.html
Copyright © 2020-2023  润新知