P3345 [ZJOI2015]幻想乡战略游戏 动态点分治
\(\color{#0066ff}{ 题目描述 }\)
傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了。
在打仗之前,幽香现在面临一个非常基本的管理问题需要解决。 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来。
在游戏中,幽香可能在空地上增加或者减少一些军队。同时,幽香可以在一个空地上放置一个补给站。 如果补给站在点u上,并且空地v上有dv个单位的军队,那么幽香每天就要花费dv*dist(u,v)的金钱来补给这些军队。
由于幽香需要补给所有的军队,因此幽香总共就要花费为Sigma(Dv*dist(u,v),其中1<=V<=N)的代价。其中dist(u,v)表示u个v在树上的距离(唯一路径的权和)。
因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。
但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。
\(\color{#0066ff}{输入格式}\)
第一行两个数n和Q分别表示树的点数和幽香操作的个数,其中点从1到n标号。 接下来n-1行,每行三个正整数a,b,c,表示a和b之间有一条边权为c的边。 接下来Q行,每行两个数u,e,表示幽香在点u上放了e单位个军队(如果e<0,就相当于是幽香在u上减少了|e|单位个军队,说白了就是du←du+e)。数据保证任何时刻每个点上的军队数量都是非负的。
\(\color{#0066ff}{输出格式}\)
对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。
\(\color{#0066ff}{输入样例}\)
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
\(\color{#0066ff}{输出样例}\)
0
1
4
5
6
\(\color{#0066ff}{数据范围与提示}\)
对于所有数据,1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5 非常神奇的是,对于所有数据,这棵树上的点的度数都不超过20,且N,Q>=1
\(\color{#0066ff}{题解}\)
这题既然给出了大方的6s,一定是道巨暴力题
动态点分治题
建立点分树,每个点维护三个值
gather[i]代表点分树中以i为根子树的所有点的军队数量 * 该点到i的dis(注意是原树上的dis)
tofa[i]表示点分树中以i为根的子树对它在点分树上的父亲的gather的贡献
tot[i]代表点分树中以i为根的子树中的军队数量
考虑当前补给站在a, 考虑移动到a的儿子b
那么a除了b的所有子树的军队都会加上一个\(dis_{ab}\)
b的子树内所有的军队都会减去一个\(dis_{ab}\)
可以发现,如果某个点更优,当且仅当外面的军队少,里面的军队多
所以一旦发现b更优,那么最后的补给站一定在b子树内
所以对于询问,我从点分树的根开始找,一旦发现孩子更优,就在孩子的子树中递归
现在考虑怎么计算一个点的答案(就是补给站在那个点的答案)
首先,初始答案是gather[i],这是子树内的贡献
现在考虑怎么统计子树外的点的贡献
设dis为当前点到补给站的距离(原树距离)
用tot[fa[i]] - tot[i]就是父亲子树中出去当前子树的部分的军队数量
用这个乘上一个dis,就是父亲与当前点的路径的贡献
这是父亲到当前点的,还有一段是军队到父亲的(军队到父亲+父亲到当前点=军队到当前点)
这部分为gather[fa[i]] - tofa[i]父亲的总贡献- 当前子树贡献就是其它点到父亲的贡献
一直统计就行了
对于修改,在点分树上向上跳(log)然后直接维护三个值就行了
\(O(nlog^2n)\)
这是写欧拉序RMQ查询距离的复杂度
如果写倍增,那复杂度就是\(O(nlog^3n)\),会TLE
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; int x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
struct node {
int to;
LL dis;
node *nxt;
node(int to = 0, LL dis = 0, node *nxt = NULL): to(to), dis(dis), nxt(nxt) {}
void *operator new (size_t) {
static node *S = NULL, *T = NULL;
return (S == T) && (T = (S = new node[1024]) + 1024), S++;
}
};
const int maxn = 2e5 + 10;
int siz[maxn], f[maxn], root, sum, n, q, rt, fa[maxn], lg[maxn];
LL gather[maxn], tofa[maxn],tot[maxn];
LL st[maxn][26], dfn[maxn], euler, Dis[maxn];
bool vis[maxn];
node *h[maxn], *head[maxn];
void add(int from, int to, int dis, node **hh) {
hh[from] = new node(to, dis, hh[from]);
}
void dfs(int x, int F, LL dis) {
Dis[x] = dis;
st[++euler][0] = dis, dfn[x] = euler;
for(node *i = h[x]; i; i = i->nxt) {
if(i->to == F) continue;
dfs(i->to, x, dis + i->dis);
st[++euler][0] = dis;
}
}
void ST() {
dfs(1, 0, 0);
lg[0] = -1;
for(int i = 1; i < maxn; i++) lg[i] = lg[i >> 1] + 1;
for(int j = 1; (1 << j) <= euler; j++)
for(int i = 1; i + (1 << j) - 1 <= euler; i++)
st[i][j] = std::min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
LL getdis(int x, int y) {
if(dfn[x] > dfn[y]) std::swap(x, y);
int len = lg[dfn[y] - dfn[x] + 1];
return Dis[x] + Dis[y] - (std::min(st[dfn[x]][len], st[dfn[y] - (1 << len) + 1][len]) << 1);
}
void getroot(int x, int F) {
siz[x] = 1;
f[x] = 0;
for(node *i = h[x]; i; i = i->nxt) {
if(i->to == F || vis[i->to]) continue;
getroot(i->to, x);
siz[x] += siz[i->to];
f[x] = std::max(f[x], siz[i->to]);
}
f[x] = std::max(f[x], sum - siz[x]);
if(f[x] < f[root]) root = x;
}
void build(int x) {
vis[x] = true;
for(node *i = h[x]; i; i = i->nxt) {
if(vis[i->to]) continue;
sum = siz[i->to], root = 0;
getroot(i->to, x);
add(x, root, i->to, head);
fa[root] = x;
build(root);
}
}
void add(int pos, LL val) {
tot[pos] += val;
for(int i = pos; fa[i]; i = fa[i]) {
LL D = getdis(pos, fa[i]);
tofa[i] += D * val;
tot[fa[i]] += val;
gather[fa[i]] += D * val;
}
}
LL calc(int x) {
LL res = gather[x];
for(int i = x; fa[i]; i = fa[i]) {
LL D = getdis(fa[i], x);
res += (tot[fa[i]] - tot[i]) * D;
res += gather[fa[i]] - tofa[i];
}
return res;
}
LL query(int x) {
LL res = calc(x);
for(node *i = head[x]; i; i = i->nxt)
if(calc(i->dis) < res) return query(i->to);
return res;
}
signed main() {
n = in(), q = in();
int x, y, z;
for(int i = 1; i < n; i++) {
x = in(), y = in(), z = in();
add(x, y, z, h), add(y, x, z, h);
}
f[0] = sum = n;
getroot(1, 0);
int rt = root;
build(root);
ST();
while(q --> 0) {
x = in(), y = in();
add(x, y);
printf("%lld\n", query(rt));
}
return 0;
}
P3345 [ZJOI2015]幻想乡战略游戏 动态点分治的更多相关文章
- 洛谷P3345 [ZJOI2015]幻想乡战略游戏 [动态点分治]
传送门 调了两个小时,终于过了-- 凭啥人家代码80行我180行啊!!! 谁叫你大括号换行 谁叫你写缺省源 思路 显然,补给点所在的位置就是这棵树的带权重心. 考虑size已知时如何找重心:一开始设答 ...
- BZOJ 3924 / Luogu P3345 [ZJOI2015]幻想乡战略游戏 (动态点分治/点分树)
题意 树的结构不变,每个点有点权,每一条边有边权,有修改点权的操作,设xxx为树中一点.求∑idist(x,i)∗a[i]\sum_idist(x,i)*a[i]i∑dist(x,i)∗a[i]的最 ...
- [ZJOI2015]幻想乡战略游戏——动态点分治
[ZJOI2015]幻想乡战略游戏 带修改下,边点都带权的重心 随着变动的过程中,一些子树内的点经过会经过一些公共边.考虑能不能对这样的子树一起统计. 把树上贡献分块. 考虑点分治算法 不妨先把题目简 ...
- 【BZOJ3924】[Zjoi2015]幻想乡战略游戏 动态树分治
[BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网 ...
- [BZOJ3924][ZJOI2015]幻想乡战略游戏(动态点分治)
题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打 ...
- 【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态点分治
题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打 ...
- ZJOI2015 幻想乡战略游戏 动态点分治_树链剖分_未调完
Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来, ...
- BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)
这种动态点分治嘛,GDKOI时听打到了,也有同学讲到了,所以印象比较深刻也就想出来了,然后就在实现方面卡了好久= = 不得不说CLJ说得真的太简单了,实现方面根本没提. 首先我们可以先用树分治构建出这 ...
- bzoj 3924 [Zjoi2015]幻想乡战略游戏——动态点分治(暴力移动找重心)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3924 度数只有20,所以从一个点暴力枚举其出边,就能知道往哪个方向走. 知道方向之后直接走到 ...
随机推荐
- HTML5实用知识点
本文讲解HTML5实用知识点 新增的表单type Canvas使用 SVG使用 Audio使用 Video使用 网页缓存 文件缓存 后台worker Server-Sent Events 定位 拖放功 ...
- Socket中常见的几个转换函数(htonl,htons,ntohl,ntohs,inet_addr,inet_ntoa)
Socket中常见的几个转换函数(htonl,htons,ntohl,ntohs,inet_addr,inet_ntoa) htonl() htons() ntohl() ntohs()及inet_n ...
- 10-12C#基础--运算符
10-12C#基础--运算符 课前作业:班级内人数的姓名和年龄,分别写出之后并汇总. 一.运算符的分类 1.数学运算符(7个) 1)+(加号) 例: 2)-(减号) 例: 3)*(乘号) 例: 4)/ ...
- oracle 基础 执行sql文件
Oracle执行外部文件: c:>sqlplus user/pwd@db sql>@new.sql 执行多个sql文件: 1.把所有的文件都放在同一个目录下,然后在命令行里执行命令: ...
- Shell编程进阶 2.0 shell中断继续退出
break continue exit break 结束本次for循环 写个for循环脚本 vim for2.sh #!/bin/bash ## 5` do echo $i ] then b ...
- C语言学习笔记--动态内存分配
1. 动态内存分配的意义 (1)C 语言中的一切操作都是基于内存的. (2)变量和数组都是内存的别名. ①内存分配由编译器在编译期间决定 ②定义数组的时候必须指定数组长度 ③数组长度是在编译期就必须确 ...
- Oracle merge into 语句进行insert或者update操作,如果存在就update,如果不存在就insert
merge into的形式: MERGE INTO [target-table] A USING [source-table sql] B ON([conditional expression] ...
- render tree与css解析
浏览器在构造DOM树的同时也在构造着另一棵树-Render Tree,与DOM树相对应暂且叫它Render树吧,我们知道DOM树为javascript提供了一些列的访问接口(DOM API),但这棵树 ...
- css中字间距调整(转)
css中字间距调整 (2014-01-02 13:51:07) 标签: 汉字 英文 间距 letter-spacing word-spacing 杂谈 分类: 网页制作 汉字间字间距设置:letter ...
- 机器人自主移动的秘密,从SLAM技术说起(一)
博客转载自:https://www.leiphone.com/news/201609/c35bn1M9kgVaCCef.html 雷锋网(公众号:雷锋网)按:本文作者SLAMTEC(思岚科技公号sla ...