[多校 NOIP 联合模拟 11.30 T4] ZZH 的旅行(李超树合并) | 错题本

文章目录

题目

给一棵第 i i i 条边边权为 d i d_i di 的有根树, 1 1 1 为根。对于每个点 x x x,对于满足如下条件的序列 { s 1 , ⋯   , s k } \{s_1,\cdots,s_k\} {s1,,sk}

  1. s i − 1 s_i - 1 si1 s i s_i si 的祖先,且 s i − 1 ≠ s i s_{i - 1} \neq s_i si1=si
  2. s 1 = x s_1 = x s1=x 中。

∑ i = 2 k ( a s i − 1 − dist ( s i − 1 , s i ) ) b s i \sum_{i = 2}^k (a_{s_{i - 1}} - \text{dist}(s_{i - 1}, s_i))b_{s_i} i=2k(asi1dist(si1,si))bsi 的最大值。

1 ≤ n ≤ 1 0 6 , 0 ≤ a i , b i , d i ≤ 1 0 9 1 \leq n \leq 10^6, 0 \leq a_i, b_i, d_i \leq 10^9 1n106,0ai,bi,di109,从 1 1 1 出发到每个点的距离不超过 1 0 9 10^9 109,最终答案不超过 2 × 1 0 17 2 \times 10^{17} 2×1017

分析

考虑朴素的 DP: d p u = max ⁡ v ∈ subtree of  u d p v + ( a u − dist ( u , v ) ) b v dp_u = \max_{v \in \text{subtree of }u}dp_v + (a_u - \text{dist}(u,v))b_v dpu=vsubtree of umaxdpv+(audist(u,v))bv dist \text{dist} dist 用深度表示可以得到 d p u = max ⁡ v ∈ subtree of  u ( d p v + a u b v + dep u b v − dep v b v ) = max ⁡ v ∈ subtree of  u ( ( a u + dep u ) b v + d p v − dep v b v ) \begin{aligned} dp_u =& \max_{v \in \text{subtree of }u} (dp_v + a_ub_v + \text{dep}_ub_v -\text{dep}_vb_v) \\ =& \max_{v \in \text{subtree of }u} ((a_u + \text{dep}_u)b_v + dp_v -\text{dep}_vb_v)\end{aligned} dpu==vsubtree of umax(dpv+aubv+depubvdepvbv)vsubtree of umax((au+depu)bv+dpvdepvbv) 设函数 f u ( x ) = b u x + d p u − dep u b u f_u(x) = b_ux + dp_u -\text{dep}_ub_u fu(x)=bux+dpudepubu,上式转化为 d p u = max ⁡ v ∈ subtree of  u f v ( a u + dep u ) dp_u = \max_{v \in \text{subtree of }u} f_v(a_u + \text{dep}_u) dpu=vsubtree of umaxfv(au+depu) 由于 f u ( x ) f_u(x) fu(x) 是一次函数,很容易想到用李超树维护。一开始想的是用 DFN 序转为区间查询,然后发现李超树不能可持久化(求的是最大值,没有可减性),后来发现只需要合并所有子树,再插入当前点对应的直线,就能得到当前点的李超树了,写动态开点李超树,合并两个树 u , v u, v u,v 方法类似于权值线段树:把 v v v 的每个直线插入进 u u u。时间复杂度感觉是 O ( n log ⁡ 2 n ) O(n \log^2 n) O(nlog2n) 的,然而题解似乎说是 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的,没看懂:
时间复杂度
不管了反正加个按秩合并跑的飞快……

代码

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>

namespace IO { // 板子是 down 里面发的
	const int sz = 1 << 22;
	char a[sz + 5], b[sz + 5], *p1 = a, *p2 = a, *t = b, p[105];
	inline char gc() {
		return p1 == p2 ? (p2 = (p1 = a) + fread(a, 1, sz, stdin), p1 == p2 ? EOF : *p1++) : *p1++;
	}
	template <class T>
	T Read() {
		T x = 0;
		char c = gc();
		for (; c < '0' || c > '9'; c = gc())
			;
		for (; c >= '0' && c <= '9'; c = gc()) x = x * 10 + (c - '0');
		return x;
	}
	inline void flush() { fwrite(b, 1, t - b, stdout), t = b; }
	inline void pc(char x) {
		*t++ = x;
		if (t - b == sz)
			flush();
	}
	template <class T>
	void Print(T x, char c = '\n') {
		if (x == 0)
			pc('0');
		int t = 0;
		for (; x; x /= 10) p[++t] = x % 10 + '0';
		for (; t; --t) pc(p[t]);
		pc(c);
	}
	struct F {
		~F() { flush(); }
	} f;
}

using IO::Print;
using IO::Read;

typedef long long LL;

const int MAXN = 1000000;
const LL INF = 1ll << 58;

int N, A[MAXN + 5], B[MAXN + 5];

struct Edge {
    int v, w;
    Edge *nxt;
} E[2 * MAXN + 5], *Adj[MAXN + 5], *EdgeCnt = E;

void AddEdge(const int &u, const int &v, const int &w) {
    (++EdgeCnt)->v = v, EdgeCnt->w = w, EdgeCnt->nxt = Adj[u], Adj[u] = EdgeCnt;
}

int Dep[MAXN + 5];

struct Line {
    int k;
    LL b;

    Line() {}
    Line(const int _k, const LL &_b) { k = _k, b = _b; }
    inline LL Cal(const int &x) const { return (LL)k * x + b; }
} L[MAXN + 5];

struct LiChaoTree {
    int NodeCnt;
    struct Node {
        int lch, rch, siz;
        Line l;
    } T[MAXN * 30 + 5];

    void Insert(int &u, const int &lft, const int &rgt, Line cur) {
        if (!u) {
            T[u = ++NodeCnt].l = cur;
            T[u].siz = 1;
            return;
        }
        int mid = ((rgt - lft) >> 1) + lft;
        if (T[u].l.Cal(mid) < cur.Cal(mid))
            std::swap(T[u].l, cur);
        LL lhc = cur.Cal(lft), rhc = cur.Cal(rgt);
        LL lhu = T[u].l.Cal(lft), rhu = T[u].l.Cal(rgt);
        if (lhc <= lhu && rhc <= rhu)
            return;
        if (lhc > lhu)
            Insert(T[u].lch, lft, mid, cur);
        else
            Insert(T[u].rch, mid + 1, rgt, cur);
        T[u].siz = T[T[u].lch].siz + T[T[u].rch].siz;
    }

    int Merge(int u, const int &v, const int &lft, const int &rgt) {
        if (!u || !v)
            return u ^ v;
        Insert(u, lft, rgt, T[v].l);
        int mid = ((rgt - lft) >> 1) + lft;
        T[u].lch = Merge(T[u].lch, T[v].lch, lft, mid);
        T[u].rch = Merge(T[u].rch, T[v].rch, mid + 1, rgt);
        T[u].siz = T[T[u].lch].siz + T[T[u].rch].siz;
        return u;
    }

    LL Query(const int &u, const int &lft, const int &rgt, const int &x) {
        if (!u) return 0;
        if (lft == rgt) {  return T[u].l.Cal(x); }
        LL res = T[u].l.Cal(x);
        int mid = ((rgt - lft) >> 1) + lft;
        if (x <= mid) res = std::max(res, Query(T[u].lch, lft, mid, x));
        else res = std::max(res, Query(T[u].rch, mid + 1, rgt, x));
        return res;
    }
} T;

int DfnCnt;
int Root[MAXN + 5];

LL Dp[MAXN + 5];

void Solve(const int &u, const int &f) {
    for (Edge *i = Adj[u]; i; i = i->nxt) {
        int v = i->v;
        if (v == f)
            continue;
        Dep[v] = Dep[u] + i->w;
        Solve(v, u);
        if (T.T[Root[v]].siz > T.T[Root[u]].siz)
            std::swap(Root[u], Root[v]);
        Root[u] = T.Merge(Root[u], Root[v], 0, 2e9);
    }
    Dp[u] = T.Query(Root[u], 0, 2e9, A[u] + Dep[u]);
    T.Insert(Root[u], 0, 2e9, Line(B[u], -(LL)Dep[u] * B[u] + Dp[u]));
}

int main() {
    N = Read<int>();
    for (int i = 1; i <= N; i++) A[i] = Read<int>(), B[i] = Read<int>();
    for (int i = 1; i < N; i++) {
        int u = Read<int>(), v = Read<int>(), w = Read<int>();
        AddEdge(u, v, w);
        AddEdge(v, u, w);
    }
    Solve(1, 0);
    for (int i = 1; i <= N; i++) Print(Dp[i]);
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值