题意

给你一颗 \(n\) 个点的树,每个点的度数不超过 \(20\) ,有 \(q\) 次修改点权的操作。

需要动态维护带权重心,也就是找到一个点 \(v\) 使得 \(\displaystyle \sum_{v} w_v \times \mathrm{dist}(u, v)\) 最小。

数据范围

\(n \le 10^5, q \le 10^5, \forall v, w_v \ge 0\)

题解

\(\text{Update on 2019.3.29:}\) 似乎可以二叉化就可以不用保证度数了。。

首先了解一个重心的重要性质:

对于一条边 \(x \to y\) 如果 \(x\) 侧子树和 \(>\) \(y\) 侧子树和,那么重心一定在 \(x\) 侧。

值得一提的是这个结论对于 \(\mathrm{dist}^k(x, i)\) 都是成立的,一般情况下都需要快速求出树的带权重心。

利用这个结论可以快速求出 带权重心

暴力求的话,在随机数据下表现非常优秀,但是我们明显可以用一些数据结构来优化这个过程,此时不难想到 点分树

因为对于上面那个找 带权重心 的过程,我们可以考虑分治解决。

  1. 首先先到整棵树的重心,当做分治操作的起点。
  2. 每次考虑枚举它所有点分树上的儿子,然后向着 \(sumchild_y * 2 > Sum\) 的方向去走,也就是向着子树权值和大于总权值一半的方向。
  3. 然后我们接下来就可以到这个子树所对应的分治中心,继续进行分治操作。
  4. 直到这个点在点分树上不存在一个儿子满足 \(sumchild_y * 2 > Sum\) 的要求,此时这个点就是重心。

这样我们最多重复 \(\log n\) 次操作就停下来,接下来我们就是需要动态求一个子树的 \(sum_y\) 。

这个显然可以用树剖线段树等数据结构进行维护,但我们有了点分树显然这样是多余的。

我们考虑对于每个点分树上每个点,维护它子树所有点的 \(w_i\) 的和,记为 \(\displaystyle sum_i = \sum_{v \in child(i)} w_v\) 。

我们每次从 \(x \to y\) 向下分治的时候,令 \(v\) 为 \(x \to y\) 在 原树 路径上除 \(x\) 外第一个点。

我们考虑把 \(v \to y\) 在点分树路径上的所有 \(sum\) 加上 \(sum_x - sum_y\) 也就是 \(x\) 部分的点权。

至于这样为什么是对的。简单说明下,这样就会对接下来所有需要算上 \(x\) 部分贡献的分治重心进行贡献。这样我们就保证了所有要算上的点都是算上的正确的答案。

注意做完后需要减回来。


然后我们找到了重心,考虑计算答案。

有两种方法。

  1. 第一种是类似于 「HNOI2015」开店 其中一个做法,利用满足差分的性质。

    也就是 \(\displaystyle \sum _{i=1}^{n} w_i \mathrm{dist}(x, i) = \sum_{i=1}^{n} w_i(d_i + d_x) - 2 \sum_{i=1}^{n} w_i d_{lca(i, x)}\) 的特性。(此处 \(d_i\) 为 \(i\) 的深度)

    对于每个点将其到根路径链上的点加上 \(w_i\) ,然后询问 \(x\) 到根的路径点权和 \(res\)。

    用 \(\displaystyle \sum_{i=1}^{n} w_id_i + (\sum_{i=1}^{n}w_i)d_x\) 减去 \(2res\) 就行了。然后用树剖后,利用线段树就可以动态维护了。

  2. 但显然此处,我们还是有着点分树这个强大的树上结构,可以考虑换一种方式来维护。

    我们在之前维护 \(sum_u\) 的基础上,多维护两个东西。

    • \(tot_u\) :点分树上 \(u\) 的子树里所有点的到 \(u\) 的带权距离和 \(\displaystyle \sum_{v \in child(u)} w_v \times \mathrm{dist}(v, u)\)。
    • \(totfa_u\) :点分树上\(u\) 的子树里所有点的到 \(u\) 在点分树上的父亲 \(fa\) 的带权距离和 \(\displaystyle \sum_{v \in child(u)} w_v \times \mathrm{dist}(v, fa)\)。

    然后询问 \(pos\) 节点的时候。我们考虑每次在点分树向上跳,并计算贡献。

    假设当前从 \(v \to u\) ,把 \(ans\) 加上 \((sum_{u} - sum_{v}) \times \mathrm{dist}(pos,u)\) ,这个意思就是把 \(v\) 外面所有的点加上这条边权的答案。

    但是这样显然算少了,因为 \(v\) 外面所有点到 \(u\) 的距离没有算上,所以还要加上 \(tot_u - totfa_v\) 这部分贡献就行了。

    注意 \(ans\) 一开始的时候初值是 \(tot_{pos}\) 。

    然后为了代码没有那么毒瘤,对于此处的树上距离,我们可以预处理出每个点到它点分树上的祖先的距离,因为我们只需要用上这些点对的距离。

总结

对于一些动态有关树上距离的问题,我们可以考虑点分树之类的强大数据结构。

然后带权重心都可以满足之前那个性质,可以用点分树上去找。

代码

强烈建议看看我的代码!! 写的真的优秀!!

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << x << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return x * fh;
} void File() {
#ifdef zjp_shadow
freopen ("2135.in", "r", stdin);
freopen ("2135.out", "w", stdout);
#endif
} const int N = 1e5 + 1e3, M = N << 1; typedef long long ll; int Head[N], Next[M], to[M], val[M], e;
inline void add_edge(int u, int v, int w) {
to[++ e] = v; Next[e] = Head[u]; Head[u] = e; val[e] = w;
}
inline void Add(int u, int v, int w) {
add_edge(u, v, w); add_edge(v, u, w);
} #define Travel(i, u, v) for(register int i = Head[u], v = to[i]; i; v = to[i = Next[i]]) bitset<N> vis;
int sz[N], maxsz[N], rt, nodesum;
void Get_Root(int u, int fa = 0) {
sz[u] = maxsz[u] = 1;
Travel(i, u, v) if (v != fa && !vis[v])
Get_Root(v, u), sz[u] += sz[v], chkmax(maxsz[u], sz[v]);
chkmax(maxsz[u], nodesum - sz[u]);
if (maxsz[u] < maxsz[rt]) rt = u;
} ll dis[N][20]; int from[N], cur[N];
void Get_Dis(int u, ll dep, int fa, int anc) {
if (fa) from[u] = anc, dis[u][cur[u] ++] = dep;
Travel(i, u, v) if (!vis[v] && v != fa) Get_Dis(v, dep + val[i], u, anc);
} typedef pair<int, ll> PII;
#define fir first
#define sec second
#define mp make_pair
vector<PII> Sub[N]; void Dfs_Div(int u = 1) {
vis[u] = true; Get_Dis(u, 0, 0, u);
Travel(i, u, v) if (!vis[v])
rt = 0, nodesum = sz[v], Get_Root(v),
Sub[u].push_back(mp(rt, v)), from[rt] = u, Dfs_Div(rt);
} ll sum[N], tot[N], tot_fa[N];
inline void Update(int pos, int uv) {
tot_fa[pos] += dis[pos][0] * uv;
for (register int u = pos, dep = 0; u; u = from[u], ++ dep) {
sum[u] += uv;
tot[from[u]] += dis[pos][dep] * uv;
tot_fa[from[u]] += dis[pos][dep + 1] * uv;
}
} PII cache[N]; int len = 0; ll Sum;
int Find_Root(int u) {
for (PII it : Sub[u]) {
register int v = it.fir;
if (sum[v] * 2 > Sum) {
register int pos; ll sumu;
for(pos = it.sec, sumu = Sum - sum[v]; pos != from[u]; pos = from[pos])
sum[pos] += sumu, cache[++ len] = mp(pos, sumu);
return Find_Root(v);
}
}
return u;
} int bas;
inline ll Query() {
register int pos = Find_Root(bas);
For (i, 1, len) sum[cache[i].fir] -= cache[i].sec; len = 0; ll res = tot[pos];
for (register int u = from[pos], Last = pos, dep = 0; u; u = from[Last = u], ++ dep)
res += tot[u] - tot_fa[Last] + (sum[u] - sum[Last]) * dis[pos][dep]; return res;
} signed main () { File(); int n = read(), q = read();
For (i, 1, n - 1) {
int u = read(), v = read(), w = read(); Add(u, v, w);
} maxsz[rt = 0] = nodesum = n; Get_Root(1); Dfs_Div(bas = rt);
For (i, 1, n) if(cur[i]) reverse(dis[i], dis[i] + cur[i]); while (q --) {
int pos = read(), uv = read();
Sum += uv; Update(pos, uv);
printf ("%lld\n", Query());
} return 0;
}

LOJ #2135. 「ZJOI2015」幻想乡战略游戏(点分树)的更多相关文章

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

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

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

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

  3. LOJ2135 「ZJOI2015」幻想乡战略游戏

    题意 题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和 ...

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. Android为TV端助力(转载)

    作者地址http://www.jianshu.com/u/63915ef020e2 针对Android Tv的自定义RecyclerView 作者 wenju_song 关注 2016.12.09 1 ...

  2. android中的websocket 应用

    websocket 在实际的应用中不仅仅能做聊天应用,还可以利用websocket长连接保持数据的实时更新以及信息的推送. websocket 的实现的关键点 第一个:首先需要引入 java-webs ...

  3. spring学习总结——装配Bean学习四(导入和混合配置)

    情景:在典型的Spring应用中,我们可能会同时使用自动化和显式配置(JavaConfig)或者XML配置,幸好在Spring中,这些配置方案都不是互斥的.你尽可以将JavaConfig的组件扫描和自 ...

  4. vue.js的手脚架vue-cli项目搭建的步骤

    手脚架是什么? 众所周知,现在的前端项目发展得越渐越大,我们前端程序员要从0开始去搭建一套完整的项目很费时,所以这时候前端工程的手脚架就出现了. 我用得vue-cli也是其中之一,还有其他的我也说不清 ...

  5. stereoscopic 3D

    色分(Anaglyph)模式:典型的如红蓝立体,是利用红镜片只允许红光通过,蓝镜片只允许蓝光通过的原理,将两幅视差的图片(一张红色.一张蓝色)叠加构成一张立体图片 由于红蓝立体去掉了绿色分量,会导致最 ...

  6. (一)走进Metasploit渗透测试框架

    渗透测试的流程 渗透测试是一种有目的性的,针对目标机构计算机系统安全的检测评估方法,渗透测试的主要目的是改善目标机构的安全性.渗透测试各个阶段的基本工作: 1.前期交互阶段 在这个阶段,渗透测试工程师 ...

  7. ThinkPHP中使用聚合查询去重求和

    我使用的是TP5.1 首先去model类里面设置failed条件: 想要的效果: 数据库展示: 代码: eturn self::alias('gr') ->join('gs_staff gs', ...

  8. LeetCode算法题-Assign Cookies(Java实现)

    这是悦乐书的第234次更新,第247篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第101题(顺位题号是455).假设你是一个很棒的父母,并想给你的孩子一些饼干.但是,你 ...

  9. Linux Collection:软件配置

    PAS Debian 9安装最新版Firefox( Firefox 58+/Quantum) Debian 9(Strech)的仓库包含的是firefox-esr(52)版本:需要安装最新版,有如下两 ...

  10. Ajax概述和判断用户名是否存在的简单代码练习

    在本代码中主要体现,Ajax实现了部分位置的刷新.不需要重新刷新网页,重新请求服务器.下面用过代码来对Ajax更深的认识 这里需要创建,一个jsp文件(显示登录界面),js文件(对Ajax的主要设置) ...