BZOJ5210 最大连通子块和

Address


Solution

  • a [ i ] a[i] 表示点 i i 的权值, f [ i ] f[i] 表示包含点 i i 的最大连通子块, g [ i ] = v s o n [ i ] f [ v ] g[i] = \sum \limits_{v \notin son[i]} f[v] ,转移有: f [ i ] = max { 0 , a [ i ] + g [ i ] + f [ s o n [ i ] ] } f[i] = \max\{0, a[i] + g[i] + f[son[i]]\}
  • 如果这题每次只询问 f [ 1 ] f[1] 的话就是裸的 DDP 了。
  • 不过这题好像还是挺裸的
  • 事实上我们要求的是 max i = 1 n { f [ i ] } \max\limits_{i = 1}^{n}\{f[i]\}
  • 首先有一个思路:
  1. 对每条重链维护 max { f [ i ] } \max\{f[i]\}
  2. 因为重链的 DFS 序相邻,我们记录每条重链的起点和终点的 DFS 序,并按照终点的 DFS 序对重链排序,那么对于一棵子树包含的所有重链,它们在重链序列上必然是一段区间,可以二分得到。
  3. 显然终点在一棵子树内,而起点不在这棵子树内的重链最多只有一条,我们用 RMQ 维护起点 DFS 序最小的重链编号,对这条重链单独查询 m a x { f [ i ] } max\{f[i]\} ,其余可以用线段树维护区间最大值查询。
  4. 修改一个点的权值时,所涉及到的重链只有 O ( log n ) O(\log n) 条,每次在线段树上修改 max { f [ i ] } \max\{f[i]\} 即可。
  • 现在我们只剩下一个问题:怎样维护每条重链的 max { f [ i ] } \max\{f[i]\}
  • 观察转移方程,我们把 a [ i ] + g [ i ] a[i] + g[i] 看做点 i i 的权值, max { f [ i ] } \max\{f[i]\} 即为求重链上的最大子段和,这是线段树的经典问题。
  • 涉及修改一点的祖先的 g [ i ] g[i] ,相当于查询当前这条重链的最大前缀和,同样可以用线段树实现。
  • 这样,我们就做到了单次修改 O ( log 2 n ) O(\log^2 n) ,单次询问 O ( log n ) O(\log n) 解决了这一问题。

Code

  • 由于再写了一个线段树,代码十分地长……
  • 一个卡常小技巧:线段树维护最大子段和时可能要上传结构体,实际运行较慢;考虑到本题询问的区间是固定的,我们先预处理出这些区间,询问时 f o r for 一遍合并区间信息即可。
#include <iostream> 
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
  
template <class T>
inline void read(T &res)
{
    char ch; bool flag = false; res = 0;
    while (ch = getchar(), !isdigit(ch) && ch != '-');
    ch == '-' ? flag = true : res = ch ^ 48;
    while (ch = getchar(), isdigit(ch))
        res = res * 10 + ch - 48;
    flag ? res = -res : 0;
}
  
const int S = 1 << 20;
char fwt[S], *ohead = fwt;
const char *otail = ohead + S;
  
inline void outChar(char ch)
{
    if (ohead == otail)
        fwrite(fwt, 1, S, stdout), ohead = fwt;
    *ohead++ = ch;  
}
  
template <class T>
inline void put(T x)
{
    if (x > 9) put(x / 10);
    outChar(x % 10 + 48); 
}
  
typedef long long ll;
const int L = 8e5 + 5;
const int N = 2e5 + 5;
const int M = 4e5 + 5;
int idt[N], fa[N], top[N], dep[N], pos[N], idx[N], sze[N], son[N];
int n, m, T, qm, id;
int va[N], ed[N], Log[N], g[18][N], _seg[N][18]; 
ll val[N], f[N], mx[L];
  
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
  
struct seg
{
    int l, r;
      
    seg() {}
    seg(int L, int R):
        l(L), r(R) {}
          
    inline bool operator < (const seg &a) const
    {
        return r < a.r;
    }
}q[N];
  
inline int idMin(int x, int y) {return q[x].l < q[y].l ? x : y;}
  
struct Tree
{   
    ll sum, pre, suf, ans;
      
    Tree() {}
    Tree(ll Sum, ll Pre, ll Suf, ll Ans):
        sum(Sum), pre(Pre), suf(Suf), ans(Ans) {}
           
    friend inline Tree operator + (const Tree &a, const Tree &b)
    {
        Tree c;
        c.sum = a.sum + b.sum;
        c.pre = Max(a.sum + b.pre, a.pre);
        c.suf = Max(a.suf + b.sum, b.suf);
        c.ans = Max(Max(a.ans, b.ans), a.suf + b.pre);
        return c;
    } 
}tr[L];
  
struct Edge
{
    int to; Edge *nxt;
}p[M], *lst[N], *P = p;
  
inline void Link(int x, int y)
{
    (++P)->nxt = lst[x]; lst[x] = P; P->to = y;
    (++P)->nxt = lst[y]; lst[y] = P; P->to = x;
}
  
inline void Dfs1(int x)
{
    dep[x] = dep[fa[x]] + 1;
    sze[x] = 1;
    f[x] = va[x];
      
    for (Edge *e = lst[x]; e; e = e->nxt)
    {
        int y = e->to;
        if (y == fa[x]) continue;
        fa[y] = x;
        Dfs1(y);
        sze[x] += sze[y];
        sze[y] > sze[son[x]] ? son[x] = y : 0;
        f[x] += Max(f[y], 0ll); 
    }
}
  
inline void Dfs2(int x)
{
    if (son[x])
    {
        pos[son[x]] = ++T;
        idx[T] = son[x];
        top[son[x]] = top[x];
        Dfs2(son[x]);
    }
    int y;
    for (Edge *e = lst[x]; e; e = e->nxt)
        if (y = e->to, !top[y])
        {
            pos[y] = ++T;
            idx[T] = y;
            top[y] = y;
            Dfs2(y);
        }
}
  
inline void Init()
{
    Dfs1(1);
    pos[1] = idx[1] = top[1] = T = 1;
    Dfs2(1);
    for (int x = 1; x <= n; ++x)
        if (top[x] == x)
        {
            int y;
            for (y = x; son[y]; y = son[y]);
            ed[x] = y;
            q[++qm] = seg(pos[x], pos[y]);
        }
}
  
#define sL s << 1
#define sR s << 1 | 1
   
inline void Uptdate(int s)
{
    tr[s] = tr[sL] + tr[sR];
}
  
inline void Build(int s, int l, int r)
{
    if (l == r)
    {
        int x = idx[l], y; ll g = va[x];
        for (Edge *e = lst[x]; e; e = e->nxt)
            if (y = e->to, y != son[x] && y != fa[x])
                g += Max(f[y], 0ll);
        val[l] = g;
        tr[s] = Tree(g, g, g, g);
        return ; 
    }
      
    int mid = l + r >> 1;
    Build(sL, l, mid);
    Build(sR, mid + 1, r);
    Uptdate(s);
}
  
inline void findSeg(int s, int l, int r, int x, int y)
{
    if (l == x && r == y)
        return (void)(_seg[id][++_seg[id][0]] = s);
    int mid = l + r >> 1;
      
    if (y <= mid)
        findSeg(sL, l, mid, x, y);
    else if (x > mid)
        findSeg(sR, mid + 1, r, x, y);
    else
    {
        findSeg(sL, l, mid, x, mid);
        findSeg(sR, mid + 1, r, mid + 1, y);
    }
}
  
inline void Modify(int s, int l, int r, int x)
{
    if (l == r)
    {
        ll g = val[x];
        tr[s] = Tree(g, g, g, g);
        return ;
    }
      
    int mid = l + r >> 1;
    x <= mid ? 
        Modify(sL, l, mid, x) : Modify(sR, mid + 1, r, x);
    Uptdate(s);
}
  
inline Tree queryTree_quick(int t)
{
    Tree res = tr[_seg[t][1]];
    for (int i = 2, im = _seg[t][0]; i <= im; ++i)
        res = res + tr[_seg[t][i]];
    return res;
}
  
inline Tree queryTree(int s, int l, int r, int x, int y)
{
    if (l == x && r == y)   
        return tr[s];
          
    int mid = l + r >> 1;
    if (y <= mid)
        return queryTree(sL, l, mid, x, y);
    else if (x > mid)
        return queryTree(sR, mid + 1, r, x, y);
    else
        return queryTree(sL, l, mid, x, mid)
             + queryTree(sR, mid + 1, r, mid + 1, y);
}
  
inline void Uptdate2(int s)
{
    mx[s] = Max(mx[sL], mx[sR]);
}
  
inline void Build2(int s, int l, int r)
{
    if (l == r) 
        return (void)(mx[s] = queryTree(1, 1, n, q[l].l, q[l].r).ans);
      
    int mid = l + r >> 1;
    Build2(sL, l, mid); Build2(sR, mid + 1, r);
    Uptdate2(s);
}
  
inline void Modify2(int s, int l, int r, int x, ll v)
{
    if (l == r) 
        return (void)(mx[s] = v);
          
    int mid = l + r >> 1;
    x <= mid ?
        Modify2(sL, l, mid, x, v) : Modify2(sR, mid + 1, r, x, v);
    Uptdate2(s);
}   
  
inline ll queryMax(int s, int l, int r, int x, int y)
{
    if (l == x && r == y)
        return mx[s];
  
    int mid = l + r >> 1;
    if (y <= mid)
        return queryMax(sL, l, mid, x, y);
    else if (x > mid)
        return queryMax(sR, mid + 1, r, x, y);
    else
        return Max(queryMax(sL, l, mid, x, mid),
                   queryMax(sR, mid + 1, r, mid + 1, y));
}
  
inline void pathModify(int x, int v)
{
    val[pos[x]] += v - va[x];
    va[x] = v;
      
    while (top[x] > 1)
    {
        int w = idt[top[x]];
        ll lst = queryTree_quick(w).pre;
        Modify(1, 1, n, pos[x]);
        Tree tmp = queryTree_quick(w);
        Modify2(1, 1, qm, w, tmp.ans);
        ll now = tmp.pre;
          
        x = fa[top[x]];
        val[pos[x]] += Max(now, 0ll) - Max(lst, 0ll);
    }
      
    int w = idt[top[x]];
    Modify(1, 1, n, pos[x]);
    Modify2(1, 1, qm, w, queryTree_quick(w).ans);
}
  
inline int queryId(int x, int y)
{
    int k = Log[y - x + 1];
    return idMin(g[k][x], g[k][y - (1 << k) + 1]);
}
  
int main()
{
    read(n); read(m);
    for (int i = 1; i <= n; ++i)
        read(va[i]);
          
    for (int i = 1, x, y; i < n; ++i)
    {
        read(x); read(y);
        Link(x, y);
    }
      
    Init();
    Build(1, 1, n); 
    std::sort(q + 1, q + qm + 1);
    for (int i = 1; i <= qm; ++i)
        id = i, findSeg(1, 1, n, q[i].l, q[i].r);
      
    Log[0] = -1;
    for (int i = 1; i <= qm; ++i)
        g[0][i] = i, Log[i] = Log[i >> 1] + 1;
    for (int j = 1, jm = Log[qm]; j <= jm; ++j)
        for (int i = 1; i + (1 << j) - 1 <= qm; ++i)
            g[j][i] = idMin(g[j - 1][i], g[j - 1][i + (1 << j - 1)]);
    for (int i = 1; i <= qm; ++i)
        idt[idx[q[i].l]] = i;
    Build2(1, 1, qm); 
      
    char opt; int x, v;
    while (m--)
    {
        while (opt = getchar(), opt != 'Q' && opt != 'M');
          
        if (opt == 'Q')
        {
            read(x); 
            int tl = std::lower_bound(q + 1, q + qm + 1, seg(0, pos[x])) - q,
                tr = std::upper_bound(q + 1, q + qm + 1, seg(0, pos[x] + sze[x] - 1)) - q - 1,
                w = queryId(tl, tr);
                          
            ll ans = 0;
            if (q[w].l < pos[x])
            {
                CkMax(ans, queryTree(1, 1, n, pos[x], q[w].r).ans);
                if (w > tl) CkMax(ans, queryMax(1, 1, qm, tl, w - 1));
                if (w < tr) CkMax(ans, queryMax(1, 1, qm, w + 1, tr));
            }
            else
                CkMax(ans, queryMax(1, 1, qm, tl, tr));
            put(ans), outChar('\n');
        }
        else
        {
            read(x); read(v);
            pathModify(x, v);
        }
    }
    fwrite(fwt, 1, ohead - fwt, stdout);
}
发布了104 篇原创文章 · 获赞 125 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/bzjr_Log_x/article/details/84729375