zoukankan      html  css  js  c++  java
  • 【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;
    }
    

     

  • 相关阅读:
    Oracle 11g SQL Fundamentals Training Introduction02
    Chapter 05Reporting Aggregated data Using the Group Functions 01
    Chapter 01Restriicting Data Using The SQL SELECT Statemnt01
    Oracle 11g SQL Fundamentals Training Introduction01
    Chapter 04Using Conversion Functions and Conditional ExpressionsConditional Expressions
    Unix时代的开创者Ken Thompson (zz.is2120.bg57iv3)
    我心目中计算机软件科学最小必读书目 (zz.is2120)
    北京将评估分时分区单双号限行 推进错时上下班 (zz)
    佳能G系列领军相机G1X
    选购单反相机的新建议——心民谈宾得K5(转)
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8711008.html
Copyright © 2011-2022 走看看