「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

分析

考虑答案的形式,发现跟带权重心非常类似。

假设当前补给站为\(u\),并强制以\(u\)为根,\(v\)为\(u\)的一个子节点,\(sumd_u\)和\(sumd_v\)分别为\(u\)的子树内的\(d\)之和以及\(v\)的子树内的\(d\)之和,\(len(u,v)\)为边\((u,v)\)的长度。

如果将补给站迁移到点\(v\),那么\(v\)的子树内的点到补给站的距离减少了\(len(u,v)\),其他的点到补给站的距离增加了\(len(u,v)\)。也就是说,补给站迁移到点\(v\)时,代价的增量为:

\[len(u,v)\times(sumd_u-sumd_v-sumd_v)
\]

整理一下,得出性质:\(u\)为根,\(v\)为\(u\)的子节点,补给站在\(v\)比\(u\)优,当且仅当:

\[2\times sumd_v>sumd_u
\]

显然满足条件的\(v\)最多只有一个。这时候,如果没有满足条件的\(v\),则\(u\)为最优位置。否则最优位置在\(v\)的子树内

一个一个跳肯定不行,所以考虑使用点分树加速跳跃过程。具体而言,如果发现\(u\rightarrow v\)更优的话,那么就递归计算\(v\)子树中的重心,即\(u\)在点分树上的儿子节点。运用换根DP的知识我们可以维护点分树上面每个节点子树的答案并且做到\(O(\log n)\)查询每个节点换根后的答案。

时间复杂度\(O(n\log n+Q\log^2 n)\)

动态点分治的作用:在点分治的过程中,一般我们面对的问题都是静态的。如果涉及到修改这类的操作,我们就希望找到我们是如何处理到当前的修改点的,换而言之,我们希望记录下点分治的过程,这样可以通过爬点分树等操作消除影响。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;
typedef pair<int,int> pii; co int N=2e5+1;
int n,m;
namespace T{ // original tree
vector<pii> e[N];
int lg[N*2],st[N*2][18],dis[N],pos[N],dfn;
void dfs(int u,int fa){
st[pos[u]=++dfn][0]=dis[u];
for(int i=0,v;i<e[u].size();++i){
if((v=e[u][i].first)==fa) continue;
dis[v]=dis[u]+e[u][i].second,dfs(v,u);
st[++dfn][0]=dis[u];
}
}
void init(){
lg[0]=-1;
for(int i=1;i<=n<<1;++i) lg[i]=lg[i>>1]+1;
dfs(1,0),assert(dfn==2*n-1);
for(int j=1;1<<j<=dfn;++j)
for(int i=1;i+(1<<j)-1<=dfn;++i)
st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
int get_dis(int u,int v){
if(pos[u]>pos[v]) swap(u,v);
int k=lg[pos[v]-pos[u]+1];
return dis[u]+dis[v]-2*min(st[pos[u]][k],st[pos[v]-(1<<k)+1][k]);
}
}
int vis[N],sum,root,siz[N],f[N],par[N];
ll down[N],up[N],sumd[N];
vector<pii> g[N];
void get_root(int u,int fa){
siz[u]=1,f[u]=0;
for(int i=0,v;i<T::e[u].size();++i){
if(vis[v=T::e[u][i].first]||v==fa) continue;
get_root(v,u);
siz[u]+=siz[v],f[u]=max(f[u],siz[v]);
}
f[u]=max(f[u],sum-siz[u]);
if(f[u]<f[root]) root=u;
}
void work(int u,int fa){
vis[u]=1,par[u]=fa;
for(int i=0,v;i<T::e[u].size();++i){
if(vis[v=T::e[u][i].first]) continue;
sum=siz[v],root=0,get_root(v,0);
g[u].push_back(pii(root,v));
work(root,u);
}
}
void ins(int u,int val){
sumd[u]+=val;
for(int i=u;par[i];i=par[i]){
int dist=T::get_dis(par[i],u);
down[par[i]]+=(ll)dist*val; // underneath ans
up[i]+=(ll)dist*val; // upward transfer
sumd[par[i]]+=val;
}
}
ll calc(int u){
ll ans=down[u];
for(int i=u;par[i];i=par[i]){
int dist=T::get_dis(par[i],u);
ans+=down[par[i]]-up[i]+dist*(sumd[par[i]]-sumd[i]);
}
return ans;
}
ll query(int u){
ll ans=calc(u);
for(int i=0;i<g[u].size();++i)
if(calc(g[u][i].second)<ans)
return query(g[u][i].first);
return ans;
}
int main(){
// freopen("LG3345.in","r",stdin);
read(n),read(m);
for(int i=1,u,v,w;i<n;++i){
read(u),read(v),read(w);
T::e[u].push_back(pii(v,w)),T::e[v].push_back(pii(u,w));
}
T::init();
sum=n,f[0]=n,get_root(1,0);
int tmp=root;work(root,0),root=tmp;
for(int u,e;m--;){
read(u),read(e),ins(u,e);
printf("%lld\n",query(root));
}
return 0;
}

LOJ2135 「ZJOI2015」幻想乡战略游戏的更多相关文章

  1. LOJ #2135. 「ZJOI2015」幻想乡战略游戏

    #2135. 「ZJOI2015」幻想乡战略游戏 链接 分析: 动态点分治,求加权重心,带修改. 考虑如果知道了一个点s,如何求答案,那么首先可以点分治的思想,求每个联通块内所有点到分治中心距离和,然 ...

  2. LOJ #2135. 「ZJOI2015」幻想乡战略游戏(点分树)

    题意 给你一颗 \(n\) 个点的树,每个点的度数不超过 \(20\) ,有 \(q\) 次修改点权的操作. 需要动态维护带权重心,也就是找到一个点 \(v\) 使得 \(\displaystyle ...

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

    题目传送门 传送门 题目大意 给定一棵树,初始点权都为0,要求支持: 修改点权 询问带权重心 询问带权重心就在点分树上跑一下就行了.(枚举跳哪个子树更优) 剩下都是基础点分治. 学了一下11-dime ...

  4. [ZJOI2015][bzoj3924] 幻想乡战略游戏 [动态点分治]

    唉:-(动态点分治的思想真是复杂...... 先码住,再做几道题再来填坑 PS:接下来的Code因为用了倍增lca所以TLE一部分,但是懒得改成RMQ了...... Code: #include< ...

  5. 洛谷 P3345 [ZJOI2015]幻想乡战略游戏 解题报告

    P3345 [ZJOI2015]幻想乡战略游戏 题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做 ...

  6. [ZJOI2015]幻想乡战略游戏——动态点分治

    [ZJOI2015]幻想乡战略游戏 带修改下,边点都带权的重心 随着变动的过程中,一些子树内的点经过会经过一些公共边.考虑能不能对这样的子树一起统计. 把树上贡献分块. 考虑点分治算法 不妨先把题目简 ...

  7. BZOJ3924 ZJOI2015 幻想乡战略游戏 【动态点分治】

    BZOJ3924 ZJOI2015 幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂 ...

  8. AC日记——[ZJOI2015]幻想乡战略游戏 洛谷 P3345

    [ZJOI2015]幻想乡战略游戏 思路: 树剖暴力转移: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 1 ...

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

    [BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网 ...

随机推荐

  1. 遍历所有子物体中renderer(渲染器)中的material(材质)

    //得到所有可渲染的子物体Renderer[] rds = transform.GetComponentsInChildren<Renderer>();//逐一遍历他的子物体中的Rende ...

  2. bluemix部署(一)简单测试,搭建样本flask程序。

    1.注册bluemix 这个略 2.登录bluemix 这个也略 3.创建组织 这个确实是和我们的思想不一样.要创建组织.为什么呢?国内的很多服务都没见过组织这个概念.貌似神符合一个中国人是条龙,十个 ...

  3. mysql数据库存储引擎及区别

    MySQL有多种存储引擎,每种存储引擎有各自的优缺点,可以择优选择使用:MyISAM.InnoDB.MERGE.MEMORY(HEAP).BDB(BerkeleyDB).EXAMPLE.FEDERAT ...

  4. (C/C++学习笔记)附页: C/C++变量的存储类型

  5. PSP0级记录2

                              上课         编写程序               课外资料               日总计     3.13 周一           ...

  6. 8.4 C++标准输入流

    参考:http://www.weixueyuan.net/view/6410.html 总结: 所谓标准输入是指从标准输入设备中输入设备,通常来讲我们可以将标准输入设备理解为键盘. cin是标准输入类 ...

  7. 栈回溯简单实现(x86)

    0x01  栈简介  首先局部变量的分配释放是通过调整栈指针实现的,栈为函数调用和定义局部变量提供了一块简单易用的空间,定义在栈上的变量不必考虑内存申请和释放.只要调整栈指针就可以分配和释放内存.   ...

  8. django本身提供了runserver,为什么不能用来部署(runserver与uWSGI的区别)

    runserver方法是调试django时经常用到的运行方式,它使用django自带的. WSGI Server 运行,主要在测试和开发使用,并且runserver 开启的方式也是单线程. uWSGI ...

  9. python 26个技巧

    26个你不知道的Python技巧   Python是目前世界上最流行的编程语言之一.因为: 1.它容易学习 2.它用途超广 3.它有非常多的开源支持(大量的模块和库) 不好意思,优达菌又啰嗦了. 本文 ...

  10. 四条命令快速在Ubuntu16.04上配置DNS服务器

    1. apt install dnsmasq -y 2. vim /etc/dnsmasq.d/resolv.conf address=/xxx.yyy.com/21.xx.xx.x 3. servi ...