[BZOJ4712]洪水

Description

\(A\) 走到一个山脚下,准备给自己造一个小屋。这时候,小 \(A\) 的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水。于是小 \(A\) 面前出现了一个瀑布。作为平民的小A只好老实巴交地爬山堵水。那么问题来了:我们把这个瀑布看成是一个?个节点的树,每个节点有权值(爬上去的代价)。小A要选择一些节点,以其权值和作为代价将这些点删除(堵上),使得根节点与所有叶子结点不连通。问最小代价。不过到这还没结束。小A的朋友觉得这样子太便宜小A了,于是他还会不断地修改地形,使得某个节点的权值发生变化。不过到这还没结束。小A觉得朋友做得太绝了,于是放弃了分离所有叶子节点的方案。取而代之的是,每次他只要在某个子树中(和子树之外的点完全无关)。于是他找到你。

\(n\le 2\times 10^5, m\le 2\times 10^5\)

Solution

第一眼动态dp,还真是的,就当锻炼码力。

首先有转移

\[ dp[u] = min\{\sum_{v\in son(u)}dp[v],\ a[u]\} \]
\(son[u]\) 为u的重儿子(和上面的son(u)不是同一个东西),\(g[u]\) 为 u 虚儿子的 \(dp\) 值之和。

\[ dp[u] = \min\{a[u], f[son[u]] + g[u]\} \]

把这个东西放在重链上,假设我们维护了重链上每个点的 \(g[u]\)\(dp[u]\) 那么查询x就查这条重链以x开头的一个类似于前缀的最小值。

修改的时候就暴力往上跳链修改点,会更新到一些点的 \(g[u]\),这些u就是两条链的交接处。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>

typedef long long LL;
typedef unsigned long long uLL;

#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DE(x) cerr << x << endl;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;

using namespace std;

inline void proc_status()
{
    ifstream t("/proc/self/status");
    cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
inline int read() 
{
    register int x = 0; register int f = 1; register char c;
    while (!isdigit(c = getchar())) if (c == '-') f = -1;
    while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
    return x * f;
}
template<class T> inline void write(T x) 
{
    static char stk[30]; static int top = 0;
    if (x < 0) { x = -x, putchar('-'); }
    while (stk[++top] = x % 10 xor 48, x /= 10, x);
    while (putchar(stk[top--]), top);
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

const int maxN = 2e5 + 2;

#define ls (x << 1)
#define rs (x << 1 | 1)
#define Lson ls, l, mid
#define Rson rs, mid + 1, r
struct Node
{
    LL g, f;
    Node() { }
    Node(LL g, LL f) : g(g), f(f) { }
    Node operator + (const Node &B) const 
    { return Node(g + B.g, min(f, g + B.f)); }
} t[maxN << 2], val[maxN];

void pushup(int x) { t[x] = t[ls] + t[rs]; }

void build(int x, int l, int r)
{
    if (l == r) { t[x] = val[l]; return; }
    int mid = l + r >> 1;
    build(Lson); build(Rson);
    pushup(x);
}

void modify(int x, int l, int r, int p)
{
    if (l == r) { t[x] = val[l]; return; }
    int mid = l + r >> 1;
    if (p <= mid) modify(Lson, p);
    else modify(Rson, p);
    pushup(x);
}

Node query(int x, int l, int r, int L, int R)
{
    if (L <= l and r <= R) { return t[x]; }
    int mid = l + r >> 1;
    if (R <= mid) return query(Lson, L, R);
    if (mid < L) return query(Rson, L, R);
    return query(Lson, L, mid) + query(Rson, mid + 1, R);
}

int n;
int ver[maxN << 1], nxt[maxN << 1], head[maxN], tot;
int fa[maxN], top[maxN], son[maxN], size[maxN], dfn[maxN], butt[maxN], dfst;

void link(int u, int v) { ver[++tot] = v, nxt[tot] = head[u], head[u] = tot; }

void dfs1(int u, int f)
{
    fa[u] = f;
    size[u] = 1;
    for (int i = head[u]; i; i = nxt[i])
        if (ver[i] != f)
        {
            dfs1(ver[i], u);
            size[u] += size[ver[i]];
            if (size[son[u]] < size[ver[i]])
                son[u] = ver[i];
        }
}

LL dp[maxN], a[maxN];

void dfs2(int u, int topf)
{
    dp[u] = 0;
    dfn[u] = ++dfst;
    top[u] = topf;
    if (son[u]) 
    {
        dfs2(son[u], topf); 
        dp[u] += dp[son[u]];
    }
    else butt[topf] = dfst, dp[u] = 1ll << 50;
    for (int i = head[u]; i; i = nxt[i])
        if (ver[i] != fa[u] and ver[i] != son[u])
        {
            dfs2(ver[i], ver[i]);
            dp[u] += dp[ver[i]];
            val[dfn[u]].g += dp[ver[i]];
        }
    chkmin(dp[u], a[u]);
}

void work(int u, LL v)
{
    a[u] += v;
    val[dfn[u]].f += v;
    while (u)
    {
        LL tmp = query(1, 1, n, dfn[top[u]], butt[top[u]]).f;
        modify(1, 1, n, dfn[u]);
        LL y = query(1, 1, n, dfn[top[u]], butt[top[u]]).f;
        u = fa[top[u]];
        val[dfn[u]].g += y - tmp;
    }
}

int main() 
{ 
    n = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read();
    for (int i = 1; i < n; ++i)
    {
        int u = read(), v = read();
        link(u, v);
        link(v, u);
    }
    dfs1(1, 0), dfs2(1, 1);
    for (int i = 1; i <= n; ++i) 
        val[dfn[i]].f = a[i];
    build(1, 1, n);
    int m = read();
    while (m--)
    {
        char op[2];
        int x;
        LL y;
        scanf("%s %d", op, &x);
        if (op[0] == 'Q')
        {
            printf("%lld\n", query(1, 1, n, dfn[x], butt[top[x]]).f);
        }
        else 
        {
            scanf("%lld", &y);
            work(x, y);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/cnyali-Tea/p/11416989.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值