BZOJ4732. [清华集训2016]数据交互(树链剖分+线段树+multiset)
题目链接
https://www.lydsy.com/JudgeOnline/problem.php?id=4732
题解
首先,一个正确性比较显然的结论是:对于一棵有根树上的两条链 \((x_1, y_1)\) 与 \((x_2, y_2)\),若两条链存在交点,必然有:\({\rm lca}_{x_1, y_1}\) 在链 \((x_2, y_2)\) 上,或者 \({\rm lca}_{x_2, y_2}\) 在链 \((x_1, y_1)\) 上。
这样,我们可以令 \(a_u\) 表示「链的两端点的 \(\rm lca\) 为点 \(u\) 」的链的权值和,\(b_u\) 表示「链经过点 \(u\) 但两端点的 \(\rm lca\) 不为点 \(u\) 」的链的权值和。那么:
- 对于链的插入操作,插入权值为 \(w\) 的链 \((x, y)\) 时,我们只需使 \(a_{{\rm lca}_{x, y}}\) 增加 \(w\),链 \((x, y)\) 上除 \({\rm lca}_{x, y}\) 的所有点的 \(b\) 增加 \(w\)
 - 对于链的删除操作,我们只需将链的权值改为 \(-w\) 即可,其余操作同插入操作
 - 对于查询操作,根据最开始给出的结论,对于一条路径 \((x, y)\),所能得到的权值和即为路径上所有结点的 \(a\) 数值之和再加上 \(b_{{\rm lca}_{x, y}}\)
 
为了方便,我们将树链剖分后结点 \(u\) 的所有子结点中,通过轻边与结点 \(u\) 相连的称为 \(u\) 的轻儿子。
首先我们考虑如何查询答案。将原树树链剖分之后,任意一条路径都可以分成三部分。令整条路径深度最小的点为 \(u\),和 \(u\) 在同一条重链上且深度最大的点为 \(v\),那么整条路径可分为:由 \(u\) 的某个轻儿子引出的一条链(可以为空)+\(u\) 至 \(v\) 的重链部分+由 \(v\) 的某个轻儿子引出的一条链(可以为空)。我们令 \(g_u\) 表示由点 \(u\) 的某个轻儿子引出的链的 \(\sum a\) 的最大值,那么一条路径的答案即为:\(g_u + g_v + b_u + w_{u, v}\)(注意这是 \(u \neq v\) 时的情况,当 \(u = v\) 时取的是点 \(u\) 的所有轻儿子引出的链的 \(\sum a\) 的最大值与次大值)。其中,\(w_{u, v}\) 表示点 \(u\) 到点 \(v\) 的路径上所有点的 \(a\) 数值之和。
除去答案式子中的 \(b_u\),那么对于一条重链而言,这就是一个连续最大和的形式,只不过两端点还应该加上 \(g_u\) 与 \(g_v\)。这样,我们就可以用线段树来维护区间连续最大和了,对于两端的 \(g\),我们在记录左右最值时处理一下即可。由于查询的是整棵树的最大权路径,我们可以用一个 multiset 来储存每条重链的连续最大和,每次查询时查询 multiset 内的最大值即可。
接下来考虑链的插入操作(删除也可看做插入权值为负的链),根据 \(a, b\) 数组的定义,每次插入一条链后 \(a\) 只需进行单点修改。而 \(b\) 数组则为区间修改(链上修改),不过注意到答案式子 \(g_u + g_v + b_u + w_{u, v}\) 中仅有 \(u\) 一个结点使用了 \(b\) 数组,换句话说,修改 \(b\) 只会对区间的左端点有影响,因此和普通的区间加操作一样,在线段树上记录区间加标记再下传即可。注意随着链上 \(a\) 数组的修改,链到根所有轻重链交替处结点的 \(g\) 数组也要修改。同时还要顺便修改答案的 multiset。
时间复杂度 \(O(n \log^2 n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define rg register
typedef long long ll;
template<typename T> inline bool checkMax(T& a, const T& b) {
  return a < b ? a = b, true : false;
}
const int N = 1e5 + 10;
struct Edge {
  Edge* next;
  int to;
  Edge () {}
  Edge (Edge* next, int to): next(next), to(to) {}
} *first[N], pool[N << 1], *pis = pool;
inline void add(int u, int v) {
  first[u] = new (pis++) Edge (first[u], v);
  first[v] = new (pis++) Edge (first[v], u);
}
int n, m, size[N], pa[N], heavy[N], top[N], dfn[N], arc_dfn[N], dfn_cnt, end_p[N], dep[N];
multiset<ll> ans, g[N];
inline void dfs1(int u, int pa) {
  size[u] = 1;
  int v = 0;
  for (Edge* now = first[u]; now; now = now->next) {
    if (now->to ^ pa) {
      ::pa[now->to] = u;
      dep[now->to] = dep[u] + 1;
      dfs1(now->to, u);
      size[u] += size[now->to];
      if (checkMax(v, size[now->to])) {
        heavy[u] = now->to;
      }
    }
  }
}
inline void dfs2(int u, int t) {
  top[u] = t;
  dfn[u] = end_p[u] = ++dfn_cnt, arc_dfn[dfn_cnt] = u;
  if (heavy[u]) {
    dfs2(heavy[u], t);
    end_p[u] = end_p[heavy[u]];
  } else {
    ans.insert(0);
  }
  for (Edge* now = first[u]; now; now = now->next) {
    if (now->to ^ pa[u] && now->to ^ heavy[u]) {
      g[u].insert(0);
      dfs2(now->to, now->to);
    }
  }
}
#define lo (o<<1)
#define ro (o<<1|1)
struct State {
  ll lv, rv, maxv, sumv;
  State () {
    lv = rv = maxv = sumv = 0;
  }
  inline State operator + (const State& a) const {
    State res;
    res.lv = max(lv, sumv + a.lv);
    res.rv = max(a.rv, a.sumv + rv);
    res.sumv = sumv + a.sumv;
    res.maxv = max(max(maxv, a.maxv), rv + a.lv);
    return res;
  }
} s[N << 2];
ll a[N << 2], addv[N << 2];
inline void add_tag(int o, ll v) {
  addv[o] += v;
  s[o].rv += v;
  s[o].maxv += v;
}
inline void push_down(int o) {
  if (addv[o]) {
    add_tag(lo, addv[o]);
    add_tag(ro, addv[o]);
    addv[o] = 0;
  }
}
inline void modify_s(int l, int r, int o, int p, ll v) {
  if (l == r) {
    int u = arc_dfn[l];
    a[o] += v, s[o].sumv = a[o];
    multiset<ll>:: iterator it = g[u].end();
    ll res = 0;
    if (g[u].size() >= 1) {
      res += *--it;
    }
    s[o].lv = a[o] + res;
    s[o].rv = a[o] + res + addv[o];
    if (g[u].size() >= 2) {
      res += *--it;
    }
    s[o].maxv = res + a[o] + addv[o];
  } else {
    int mid = l + r >> 1;
    push_down(o);
    (p <= mid) ? modify_s(l, mid, lo, p, v) : modify_s(mid + 1, r, ro, p, v);
    s[o] = s[lo] + s[ro];
  }
}
inline void modify_tag(int l, int r, int o, int ql, int qr, ll v) {
  if (ql <= l && r <= qr) {
    return add_tag(o, v);
  } else {
    int mid = l + r >> 1;
    push_down(o);
    if (ql <= mid) {
      modify_tag(l, mid, lo, ql, qr, v);
    } if (qr > mid) {
      modify_tag(mid + 1, r, ro, ql, qr, v);
    }
    s[o] = s[lo] + s[ro];
  }
}
inline State query(int l, int r, int o, int ql, int qr) {
  if (ql <= l && r <= qr) {
    return s[o];
  } else {
    int mid = l + r >> 1;
    push_down(o);
    if (qr <= mid) {
      return query(l, mid, lo, ql, qr);
    } else if (ql > mid) {
      return query(mid + 1, r, ro, ql, qr);
    } else {
      return query(l, mid, lo, ql, qr) + query(mid + 1, r, ro, ql, qr);
    }
  }
}
inline void jump(int u, ll tmp, int lca, int w) {
  for (; u; u = pa[top[u]]) {
    int l = lca ? max(dfn[lca] + 1, dfn[top[u]]) : n + 1, r = dfn[u];
    int p = pa[top[u]];
    if (p) {
      State x = query(1, n, 1, dfn[top[p]], end_p[p]);
      ans.erase(ans.lower_bound(x.maxv));
      g[p].erase(g[p].lower_bound(tmp));
      tmp = x.lv;
    }
    if (l <= r) {
      ans.erase(ans.lower_bound(query(1, n, 1, dfn[top[u]], end_p[u]).maxv));
      modify_tag(1, n, 1, l, r, w);
      ans.insert(query(1, n, 1, dfn[top[u]], end_p[u]).maxv);
    }
    if (p) {
      g[p].insert(query(1, n, 1, dfn[top[u]], end_p[u]).lv);
      modify_s(1, n, 1, dfn[p], 0);
      ans.insert(query(1, n, 1, dfn[top[p]], end_p[p]).maxv);
    }
  }
}
inline void modify(int u, int v, int w) {
  int old_u = u, old_v = v, lca;
  for (; top[u] ^ top[v]; u = pa[top[u]]) {
    if (dep[top[u]] < dep[top[v]]) {
      swap(u, v);
    }
  }
  lca = dep[u] <= dep[v] ? u : v;
  u = old_u, v = old_v;
  jump(u, query(1, n, 1, dfn[top[u]], end_p[u]).lv, lca, w);
  jump(v, query(1, n, 1, dfn[top[v]], end_p[v]).lv, lca, w);
  State x = query(1, n, 1, dfn[top[lca]], end_p[lca]);
  ans.erase(ans.lower_bound(x.maxv));
  modify_s(1, n, 1, dfn[lca], w);
  ans.insert(query(1, n, 1, dfn[top[lca]], end_p[lca]).maxv);
  jump(lca, x.lv, 0, 0);
}
int req_u[N], req_v[N], req_w[N];
int main() {
  scanf("%d%d", &n, &m);
  for (rg int i = 1; i < n; ++i) {
    int u, v; scanf("%d%d", &u, &v);
    add(u, v);
  }
  dfs1(1, 0);
  dfs2(1, 1);
  for (rg int i = 1; i <= m; ++i) {
    char cmd[4];
    scanf("%s", cmd);
    if (*cmd == '+') {
      scanf("%d%d%d", &req_u[i], &req_v[i], &req_w[i]);
      modify(req_u[i], req_v[i], req_w[i]);
    } else {
      int tim; scanf("%d", &tim);
      modify(req_u[tim], req_v[tim], -req_w[tim]);
    }
    printf("%lld\n", *ans.rbegin());
  }
  return 0;
}
												
											BZOJ4732. [清华集训2016]数据交互(树链剖分+线段树+multiset)的更多相关文章
- BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树
		
题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...
 - BZOJ4127Abs——树链剖分+线段树
		
题目描述 给定一棵树,设计数据结构支持以下操作 1 u v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 输入 第一行两个整数n和m,表示结点个数和操作数 ...
 - BZOJ2819Nim——树链剖分+线段树+Nim游戏
		
题目描述 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略 ...
 - BZOJ2157旅游——树链剖分+线段树
		
题目描述 Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但又为了节约成本,T 城的任意两个景点之间有且只有一条路 ...
 - POJ3237 Tree 树链剖分 线段树
		
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ3237 题意概括 Description 给你由N个结点组成的树.树的节点被编号为1到N,边被编号为1 ...
 - fzu 2082 过路费  (树链剖分+线段树  边权)
		
Problem 2082 过路费 Accept: 887 Submit: 2881Time Limit: 1000 mSec Memory Limit : 32768 KB Proble ...
 - BZOJ.1036 [ZJOI2008]树的统计Count ( 点权树链剖分 线段树维护和与最值)
		
BZOJ.1036 [ZJOI2008]树的统计Count (树链剖分 线段树维护和与最值) 题意分析 (题目图片来自于 这里) 第一道树链剖分的题目,谈一下自己的理解. 树链剖分能解决的问题是,题目 ...
 - 【bzoj1959】[Ahoi2005]LANE 航线规划  树链剖分+线段树
		
题目描述 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系. 星际空间站的Samuel II巨型计算 ...
 - 【BZOJ-2325】道馆之战      树链剖分 + 线段树
		
2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 1153 Solved: 421[Submit][Statu ...
 
随机推荐
- docker入门使用教程
			
Docker概念 Docker是开发人员和系统管理员 使用容器开发,部署和运行应用程序的平台.使用Linux容器部署应用程序称为容器化.容器不是新的,但它们用于轻松部署应用程序. 容器化越来越受欢迎, ...
 - 配置tensorflow环境(anaconda+jupyter notebook)
			
很早之前,tensorflow环境之前我也曾装过,但是用的不是很舒服,很多问题都不明所以然.今天想要系统地学习一下tensorflow,于是又重新搭建了一遍,这次还是踩了不少坑.特此写下此文,供有兴趣 ...
 - 多tomcat 同一个浏览器 多个项目 会导致session覆盖
			
1,多tomcat 同一个浏览器 同一个项目 会导致session覆盖 个人猜测:一个服务器中有多个Tomcat服务器多个项目,每个服务器占用不同的端口号,当在同一个浏览器里面同时打开2个系统时,一个 ...
 - 亚马逊如何变成 SOA(面向服务的架构)
			
. 亚马逊公司不仅是世界最大的网络书店,还是世界最大的云服务商.它是怎么实现从电商到云商的转变呢? 一切都是CEO杰夫·贝索斯促成的,他对市场有着超乎常人的理解和预见. 2. 2000年前后,贝索斯有 ...
 - ifconfig命令详情
			
基础命令学习目录首页 原文链接:https://blog.csdn.net/weixin_37886382/article/details/79716879 许多windows非常熟悉ipconfig ...
 - nohup命令详解
			
基础命令学习目录首页 原文链接:https://blog.csdn.net/hfismyangel/article/details/80258126 1.nohup 用途:不挂断地运行命令. 语法:n ...
 - init命令详解
			
基础命令学习目录首页 1.手动输入命令会执行相关操作 #init 0 - 停机(千万不能把initdefault 设置为0 ) #init 1 - 单用户模式 #init 2 - 多用户, ...
 - 20162314 Experiment 1: Linear structure - experiment report.
			
Experiment report of Besti course:<Program Design & Data Structures> Class: 1623 Student N ...
 - 第二次作业<1>
			
1001.A+B Format (20) ac代码 1. 解题思路 先求和,再输出. 答案区间为-2,000,000至2,000,000,将答案分为三个区段分类讨论.虽然觉得很烦但是想不出更好的方法. ...
 - spring冲刺阶段之团队工作总结
			
一.小组成员: 王俊凯(项目经理) 罗林杰(产品负责人) 王逸辉(Master) 罗凯杰 二.任务分配情况 王俊凯:生成题目的代码编写并提出编写意见 罗林杰:负责把按钮和界面内容连接到代码上及主要代码 ...