【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp
题目描述
输入
输出
样例输入
5 4
3 -2 0 3 -1
1 2
1 3
4 2
2 5
Q 1
M 4 1
Q 1
Q 2
样例输出
4
3
1
题解
树链剖分+线段树+可删除堆维护树形动态dp
如果dp是静态的,设 $f[i]$ 表示以 $i$ 为根的子树中,选出 包括 $i$ 的连通子块 或 空块 的最大点权和。那么有 $f[i]=\text{max}(v[i]+\sum\limits_{i\to j}f[j],0)$ 。所求即是子树内所有点的 $f$ 值的最大值。
当这个dp在序列上进行时,容易转化为最大连续子段和的形式。
当这个dp在树上进行时,考虑将这棵树轻重链剖分,转化为序列问题。
设 $y$ 为 $x$ 的重儿子,所有 $x$ 的轻儿子的 $f$ 值加上 $v[x]$ 为 $g[x]$ ,那么有 $f[x]=\text{max}(f[y]+g[x],0)$ 。
这个形式类似于最小连续子段和中的最小前缀和。使用线段树维护最小前缀和(在重链这一段区间的某位置选出一个点使得 不选链顶到该点父亲,其余选最大的 最大)及总和(都不选)。线段树的叶子节点的最小前缀和和总和都是 $g$ 。
修改时,首先 $v[x]$ 修改导致 $g[x]$ 修改;然后使链顶的 $f$ 值修改,影响链顶父亲的 $g$ ,再不断修改即可。
查询时,一个点的 $f$ 值就是该点到链底节点的最小前缀和。
然而答案是子树内所有 $f$ 的最大值,因此不能仅仅维护最小前缀和。
考虑重链上的部分:其实相当于每一个后缀的前缀中最大的那个,即子段中最大的那个。因此维护最大连续子段和即可直接得出链上所有点的 $f$ 的最大值。
考虑轻链上的部分:一条重链上的答案对链顶父亲有贡献,将这个答案加到链顶父亲对应叶子节点的最大连续子段和即可。即:一个点对应叶子节点初始的最大连续子段和为:该节点的 $v$ 值与该节点轻儿子所在重链的最大连续子段和的最大值。我们对每个节点再维护这个最大值即可。由于要支持修改、查询最值,因此使用可删除堆(或者STL-set)。
这样查询时查询该点到链底的最大连续子段和就是答案了。
修改的时间复杂度为 $O(\log^2n)$ ,询问的时间复杂度为 $O(\log n)$ 。
#include <queue>
#include <cstdio>
#include <algorithm>
#define N 200010
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
typedef long long ll;
struct data
{
ll sum , ls , rs , ts;
inline friend data operator+(const data &a , const data &b)
{
data ans;
ans.sum = a.sum + b.sum;
ans.ls = max(a.ls , a.sum + b.ls);
ans.rs = max(b.rs , b.sum + a.rs);
ans.ts = max(a.rs + b.ls , max(a.ts , b.ts));
return ans;
}
}a[N << 2];
struct heap
{
priority_queue<ll> A , B;
inline void push(ll x) {A.push(x);}
inline void del(ll x) {B.push(x);}
inline ll top()
{
while(!B.empty() && A.top() == B.top()) A.pop() , B.pop();
return A.top();
}
}q[N];
int n , v[N] , head[N] , to[N << 1] , next[N << 1] , cnt , fa[N] , si[N] , bl[N] , end[N] , pos[N] , tot;
ll f[N] , ms[N] , w[N];
char str[5];
inline void add(int x , int y)
{
to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs1(int x)
{
int i;
si[x] = 1;
for(i = head[x] ; i ; i = next[i])
if(to[i] != fa[x])
fa[to[i]] = x , dfs1(to[i]) , si[x] += si[to[i]];
}
void dfs2(int x , int c)
{
int i , k = 0;
bl[x] = c , pos[x] = ++tot , w[pos[x]] = v[x];
for(i = head[x] ; i ; i = next[i])
if(to[i] != fa[x] && si[to[i]] > si[k])
k = to[i];
if(k)
{
dfs2(k , c) , f[x] = f[k] , ms[x] = ms[k] , end[x] = end[k];
for(i = head[x] ; i ; i = next[i])
if(to[i] != fa[x] && to[i] != k)
dfs2(to[i] , to[i]) , w[pos[x]] += f[to[i]] , q[pos[x]].push(ms[to[i]]);
}
else end[x] = x;
f[x] = max(f[x] + w[pos[x]] , 0ll) , ms[x] = max(ms[x] , max(f[x] , q[pos[x]].top()));
}
void build(int l , int r , int x)
{
if(l == r)
{
a[x].sum = w[l] , a[x].ls = a[x].rs = max(w[l] , 0ll) , a[x].ts = max(w[l] , q[l].top());
return;
}
int mid = (l + r) >> 1;
build(lson) , build(rson);
a[x] = a[x << 1] + a[x << 1 | 1];
}
void fix(int p , int l , int r , int x)
{
if(l == r)
{
a[x].sum = w[l] , a[x].ls = a[x].rs = max(w[l] , 0ll) , a[x].ts = max(w[l] , q[l].top());
return;
}
int mid = (l + r) >> 1;
if(p <= mid) fix(p , lson);
else fix(p , rson);
a[x] = a[x << 1] + a[x << 1 | 1];
}
data query(int b , int e , int l , int r , int x)
{
if(b <= l && r <= e) return a[x];
int mid = (l + r) >> 1;
if(e <= mid) return query(b , e , lson);
else if(b > mid) return query(b , e , rson);
else return query(b , e , lson) + query(b , e , rson);
}
void modify(int x , int z)
{
data a , b;
bool flag = 0;
a.ls = w[pos[x]] , b.ls = w[pos[x]] - v[x] + z , v[x] = z;
while(x)
{
w[pos[x]] += b.ls - a.ls;
if(flag) q[pos[x]].del(a.ts) , q[pos[x]].push(b.ts);
a = query(pos[bl[x]] , pos[end[x]] , 1 , n , 1);
fix(pos[x] , 1 , n , 1);
b = query(pos[bl[x]] , pos[end[x]] , 1 , n , 1);
x = fa[bl[x]] , flag = 1;
}
}
int main()
{
int m , i , x , y;
scanf("%d%d" , &n , &m);
for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &v[i]) , q[i].push(0);
for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x);
dfs1(1) , dfs2(1 , 1);
build(1 , n , 1);
while(m -- )
{
scanf("%s%d" , str , &x);
if(str[0] == 'M') scanf("%d" , &y) , modify(x , y);
else printf("%lld\n" , query(pos[x] , pos[end[x]] , 1 , n , 1).ts);
}
return 0;
}
【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp的更多相关文章
- HDU 2460 Network(双连通+树链剖分+线段树)
HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...
- BZOJ2325[ZJOI2011]道馆之战——树链剖分+线段树
题目描述 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中 的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才 ...
- 【Codeforces827D/CF827D】Best Edge Weight(最小生成树性质+倍增/树链剖分+线段树)
题目 Codeforces827D 分析 倍增神题--(感谢T*C神犇给我讲qwq) 这道题需要考虑最小生成树的性质.首先随便求出一棵最小生成树,把树边和非树边分开处理. 首先,对于非树边\((u,v ...
- 【BZOJ-2325】道馆之战 树链剖分 + 线段树
2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 1153 Solved: 421[Submit][Statu ...
- B20J_2836_魔法树_树链剖分+线段树
B20J_2836_魔法树_树链剖分+线段树 题意: 果树共有N个节点,其中节点0是根节点,每个节点u的父亲记为fa[u].初始时,这个果树的每个节点上都没有果子(即0个果子). Add u v d ...
- BZOJ3862Little Devil I——树链剖分+线段树
题目大意: 给一棵树,每条边可能是黑色或白色(起始都是白色),有三种操作: 1.将u到v路径上所有边颜色翻转(黑->白,白->黑) 2.将只有一个点在u到v路径上的边颜色翻转 3.查询u到 ...
- fzu 2082 过路费 (树链剖分+线段树 边权)
Problem 2082 过路费 Accept: 887 Submit: 2881Time Limit: 1000 mSec Memory Limit : 32768 KB Proble ...
- [HDU3710] Battle Over Cities [树链剖分+线段树+并查集+kruskal+思维]
题面 一句话题意: 给定一张 N 个点, M 条边的无向连通图, 每条边上有边权 w . 求删去任意一个点后的最小生成树的边权之和. 思路 首先肯定要$kruskal$一下 考虑$MST$里面去掉一个 ...
- 【bzoj2238】Mst 最小生成树+树链剖分+线段树
题目描述 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在) 输入 第一行两 ...
随机推荐
- nodejs学习笔记(3)
在看了Node8的特性后的总结,后续慢慢补充. 主要是针对一些编码规范,Node的v8引擎由于不同于其他语言的解析策略,因此需要注意一些特殊的方面. 1.try/catch使用对代码运行性能影响明显, ...
- Eclipse的汉化问题
最近看了很多我周围的同学,也都是刚开始接触Eclipse,但是都头疼于eclipse的汉化问题. 好在的是,Eclipse的汉化比较简单,不用到网上自己下载汉化包,而且关于这个软件的汉化也非常的多,所 ...
- VS2017 C++操作mysql数据库
1.首先安装mysql 具体教程可以参考https://blog.csdn.net/zhouzezhou/article/details/52446608 注意安装产品的时候记得选择MySQL Con ...
- Python学习之路:NumPy进阶
import numpy as np; #创建数组的四种办法 ##可以传入任何类数组 a = np.array([0,1,2,3,4]); b = np.array((0,1,2,3,4)); c = ...
- react.js插件开发,x-dailog弹窗浮层组件
react.js插件开发,x-dailog弹窗浮层组件 我认为,每一个组件都应该有他自带的样式和属性事件回调配置.所以我会给x-dialog默认一套简单的样式,和各种默认的配置项.所有react插件示 ...
- Vue2.0原理-指令
指令是 模板解析 的续章,本文会尝试从源码的角度来理解 指令 是如何被提取和应用的. 指令的提取 指令的提取过程是在parse阶段进行的,在 parseHTML 方法中,会解析字符串模板为如下的单个a ...
- Java 集合基础知识 List/Set/Map
一.List Set 区别 List 有序,可重复: Set 无序,不重复: 二.List Set 实现类间区别及原理 Arraylist 底层实现使用Object[],数组查询效率高 扩容机制 ...
- 07-matplotlib-箱线图
import numpy as np import matplotlib.pyplot as plt ''' 箱形图(Box-plot)又称为盒须图,盒式图,或 箱线图: 是一种用在显示一组数据分散情 ...
- PHP 预定义变量
1.$_SERVER <?php $a=$_SERVER; var_dump($a); ?> 2.$_FILES <?php if($_FILES){ echo "< ...
- Vue.js 相关知识(组件)
1. 组件介绍 组件(component),vue.js最强大的功能之一 作用:封装可重用的代码,通常一个组件就是一个功能体,便于在多个地方都能调用该功能体 根组件:我们实例化的Vue对象就是一个组件 ...