luogu P3345 [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)。数据保证任何时刻每个点上的军队数量都是非负的。

输出格式

对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。



题解来自

建点分树,我们把点分治中每一个重心的父亲设为上一层的重心

每次查询只需要从根节点(整棵树的重心)开始,看哪一个(原树中)儿子更优,如果优就往儿子对应的子树的重心上走

再考虑修改,只用把自己在点分树中的祖先修改就行了

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
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];
inline 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;
        }
}
inline 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);
}
inline 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;
}
inline 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;
}
inline 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],sumd[MAXN],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]] +=val * len;
        sumf[i] +=val * len;
    }
}
inline LL Count(int u){
    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;
        res+=(sumd[Fa[i]]-sumf[i]);
    }
    return res;
}
inline 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;
}
signed 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));
    }
}

原文地址:https://www.cnblogs.com/naruto-mzx/p/12123700.html

时间: 2024-10-07 11:58:03

luogu P3345 [ZJOI2015]幻想乡战略游戏 |动态点分治的相关文章

luogu P3345 [ZJOI2015]幻想乡战略游戏(点分树)

题意自己看... 思路 没想到今(昨)天刷着刷着点分治的水题,就刷出来了一个点分树... 然后就疯狂地找题解,代码,最后终于把它给弄懂了. 点分树--动态点分治,对于此题来说,我们发现设u为当前的补给站位置,v是它的一个儿子.同时设dis(i,j)为树上i点到j点的距离.sumi为以i为跟的子树中d(也就是军队数)的总量 我们把补给站从u转移到v,答案的变化为dis(u,v)*(sumu-sumv)-dis(u,v)*sumv=dis(u,v)*(sumu-2*sumv) 所以当2*sumv>s

【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态树分治

题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了. 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决. 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来.在游戏中,幽香可能在空地上增加或者减少一些军队.同时,幽香可以在一个空地上放置一个补给站. 如果补给站在点u上,并

BZOJ 3924 Zjoi2015 幻想乡战略游戏 动态树分治

题目大意:给定一棵树,每个点有一个点权,多次改变某个点的点权,多次查询带权重心到所有点的带权距离之和 此生无悔入东方,来世愿生幻想乡 首先我们考虑如何计算一个点到所有点的带权距离之和且支持修改 用动态树分治就好了嘛... 每个点记录子树中带权距离之和,以及权值之和,再在每个子树中记录一个需要减掉的版本 然后一直向上扫到根就能统计了 ↑这段话面对会写动态树分治的人,不会的先去切捉迷藏吧 然后就好搞了... 对于分治结构的每一个点,我们枚举它的出边 如果某条出边连向的点的距离之和小于当前点,那么答案

BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)

这种动态点分治嘛,GDKOI时听打到了,也有同学讲到了,所以印象比较深刻也就想出来了,然后就在实现方面卡了好久= = 不得不说CLJ说得真的太简单了,实现方面根本没提. 首先我们可以先用树分治构建出这棵树的分治树,也就是把这棵树的重心作为根节点然后子树为他的子树的重心这样递归下去,然后每个节点存的是其子树的信息. 对于每个节点我们保存这个子树的dv的总和已经把该节点作为点的答案值 这样对于修改能在log n的时间内解决 寻找答案的时候,我们可以发现,如果现在节点的子树dv和*2大于总节点,那么向

[ZJOI2015]幻想乡战略游戏 - 动态点分治

先考虑无修要怎么操作. 发现在无修的情况下,我们可以用一个换根\(dp\)解决. 那么带修改的情况要怎么办呢? 每次修改重新\(dp\)一遍不就行了(雾. 好的,让我们先来敲一个\(O(N^2)\)的\(dp\). #include <bits/stdc++.h> using namespace std; typedef long long ll; inline ll ty() { char ch = getchar(); ll x = 0, f = 1; while (ch < '0'

loj 2135 「ZJOI2015」幻想乡战略游戏 - 动态点分治

题目传送门 传送门 题目大意 给定一棵树,初始点权都为0,要求支持: 修改点权 询问带权重心 询问带权重心就在点分树上跑一下就行了.(枚举跳哪个子树更优) 剩下都是基础点分治. 学了一下11-dimensional的2.2k动态点分治,然后写抄出来只有1.9k??? Code /** * loj * Problem#2135 * Accepted * Time: 4492ms * Memory: 28404k */ #include <bits/stdc++.h> using namespac

P3345 [ZJOI2015]幻想乡战略游戏

传送门 考虑先随便找一个点作为根,然后再慢慢移动根,这样一步步走到最优的点 设 $sum[x]$ 表示节点 $x$ 的子树的军队数,$len(x,y)$ 表示 $x,y$ 之间边的长度 那么对于根节点 $x$ 的一个儿子 $v$,考虑把儿子搞为根时,代价的改变量 $v$ 的子树内的军队消耗减少,共减少了 $sum[v]\cdot len(x,v)$ $v$ 的子树外的军队消耗增加,即根节点 $x$ 的子树内除了 $v$ 子树的军队消耗增加 代价增加了 $(sum[x]-sum[v])\cdot

[ZJOI2015]幻想乡战略游戏 解题报告 (动态点分治)

[ZJOI2015]幻想乡战略游戏 题意 有一棵大小为 \(n\) 的带权树, 每个点有一个权值, 权值可以修改 \(q\) 次, 找出一个补给点 \(x\) , 使得 \(\sum_{u \in V} val[u] \times dis(x,u)\) 最小, 并求出这个最小值. 一句话 : 求带权重心 (zsy说的) 附加条件 : 树中所有点的度数不超过 \(20\). 思路 一道你以为复杂度过不了, 但其实是过得了的题. 首先, 我们假定先选了一个点 \(u\) 作为补给点, 我们可以算出它

bzoj3924 [Zjoi2015]幻想乡战略游戏 点分树,动态点分

[BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了. 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决. 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来.在游戏中,幽香可能在空地上增加或者减少一些军