• 【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp


    题目描述

    给出一棵n个点、以1为根的有根树,点有点权。要求支持如下两种操作:
    M x y:将点x的点权改为y;
    Q x:求以x为根的子树的最大连通子块和。
    其中,一棵子树的最大连通子块和指的是:该子树所有子连通块的点权和中的最大值
    (本题中子连通块包括空连通块,点权和为0)。

    输入

    第一行两个整数n、m,表示树的点数以及操作的数目。
    第二行n个整数,第i个整数w_i表示第i个点的点权。
    接下来的n-1行,每行两个整数x、y,表示x和y之间有一条边相连。
    接下来的m行,每行输入一个操作,含义如题目所述。保证操作为M x y或Q x之一。
    1≤n,m≤200000 ,任意时刻 |w_i|≤10^9 。

    输出

    对于每个Q操作输出一行一个整数,表示询问子树的最大连通子块和。

    样例输入

    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]= ext{max}(v[i]+sumlimits_{i o j}f[j],0)$ 。所求即是子树内所有点的 $f$ 值的最大值。

    当这个dp在序列上进行时,容易转化为最大连续子段和的形式。

    当这个dp在树上进行时,考虑将这棵树轻重链剖分,转化为序列问题。

    设 $y$ 为 $x$ 的重儿子,所有 $x$ 的轻儿子的 $f$ 值加上 $v[x]$ 为 $g[x]$ ,那么有 $f[x]= ext{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
    " , query(pos[x] , pos[end[x]] , 1 , n , 1).ts);
    	}
    	return 0;
    }
    

     

  • 相关阅读:
    java往文本文件中写入信息并修改
    idea中写servlet时报错--关于405错误
    关于引入js文件乱码的问题
    关于HTML的引入CSS文件问题
    后缀算法
    vim中 E212:无法打开并写入文件的解决办法
    Restful风格API
    Restful概念
    Andriod实现推送的解决方案(转)
    IOS系统推送原理
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8711008.html
Copyright © 2020-2023  润新知