CF932F(李超线段树+dp)

CF932F(李超线段树+dp)

此题又是新玩法, 李超线段树合并优化\(dp\)

一个显然的\(\Theta(n^2)dp\): \(dp[x]\)表示从x出发到叶子节点的最小代价

\(dp[x] = \min(dp[y] + a[x] * b[y]) ~~(y \in subtree(x))\)

如果我们将\(b[y]\)看成斜率, \(dp[y]\)看成纵截距, \(a[x]\)看成横坐标, 那么问题转为了在平面上有一些直线, 选出与直线\(x = a[x]\)相交的最靠下的点吗, 李超线段树板题, 但这道题出到了树上所以要用上线段树合并 因为有负数所以要整体右移一下, 相应的直线也需变换, 具体见代码

代码:

#pragma GCC optimize(3)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define re register
#define ll long long
using namespace std;

template <typename T>
void read(T &x) {
    x = 0; bool f = 0;
    char c = getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
    for (;isdigit(c);c=getchar()) x=x*10+(c^48);
    if (f) x=-x;
}

template <typename T>
void write(T x) {
    if (x < 0) putchar('-'), x = -x;
    if (x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 200500;
const int Delta = 100005;
const int Len = Delta << 1;

int h[N], ne[N<<1], to[N<<1];
int T[N], tot, cnt;
inline void add(int x, int y) {
    ne[++tot] = h[x], to[tot] = y, h[x] = tot;
}

int ls[N<<5], rs[N<<5], p[N<<5];
ll n, ans[N], a[N], b[N];
struct node {
    ll k, b;
}line[N];

inline ll calc(int num, ll x) {
    return line[num].k * x + line[num].b;
}

void insert(int &x, int l, int r, int num) {
    if (!x) return x = ++cnt, p[x] = num, void();
    int mid = (l + r) >> 1;
    if (calc(p[x], mid) > calc(num, mid)) swap(p[x], num);
    if (calc(p[x], l) <= calc(num, l) &&
        calc(p[x], r) <= calc(num, r)) return;
    if (calc(p[x], l) > calc(num, l)) insert(ls[x], l, mid, num);
    else insert(rs[x], mid + 1, r, num);
}

const ll INF = 0x7fffffffffff;
ll query(int now, int l, int r, ll x) {
    if (!now) return INF;
    int mid = (l + r) >> 1;
    return min(calc(p[now], x), x <= mid ?
        query(ls[now], l, mid, x) : query(rs[now], mid + 1, r, x));
}

int merge(int x, int y, int l, int r) {
    if (!x || !y) return x | y;
    insert(x, l, r, p[y]);
    int mid = (l + r) >> 1;
    ls[x] = merge(ls[x], ls[y], l, mid);
    rs[x] = merge(rs[x], rs[y], mid + 1, r);
    return x;
}

void dfs(int x, int fa) {
    for (re int i = h[x], y; i; i = ne[i]) {
        if ((y = to[i]) == fa) continue;
        dfs(y, x);
        T[x] = merge(T[x], T[y], 1, Len);
    }
    ans[x] = query(T[x], 1, Len, a[x] + Delta);
    if (ans[x] == INF) ans[x] = 0;
    line[x] = (node){b[x], ans[x] - b[x] * Delta};
    insert(T[x], 1, Len, x);
}

int main() {
    read(n);
    for (re int i = 1;i <= n; i++) read(a[i]);
    for (re int j = 1;j <= n; j++) read(b[j]);
    for (re int i = 1;i < n; i++) {
        int x, y; read(x), read(y);
        add(x, y), add(y, x);
    } dfs(1, 0);
    for (re int i = 1;i <= n; i++)
        write(ans[i]), putchar(' ');
    return 0;
}

原文地址:https://www.cnblogs.com/Hs-black/p/12271622.html

时间: 2024-08-03 01:51:52

CF932F(李超线段树+dp)的相关文章

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

ZOJ3632 线段树+DP

买西瓜吃,每个西瓜有两个参数,一个是p代表价格,一个是t代表能吃几天,要求n天每天都能吃西瓜,而且如果你今天买了,以前买的还没吃完 那么都得扔了,求最小花费,还真想不到用线段树+DP,最后看了一下别人的标题,想了一下,DP方程挺好推的,线段树也只是单点查询, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #inclu

【BZOJ-1568】Blue Mary开公司 李超线段树 (标记可持久化)

1568: [JSOI2008]Blue Mary开公司 Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 557  Solved: 192[Submit][Status][Discuss] Description Input 第一行 :一个整数N ,表示方案和询问的总数. 接下来N行,每行开头一个单词“Query”或“Project”. 若单词为Query,则后接一个整数T,表示Blue Mary询问第T天的最大收益. 若单词为Project,则后

【BZOJ-4515】游戏 李超线段树 + 树链剖分 + 半平面交

4515: [Sdoi2016]游戏 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 304  Solved: 129[Submit][Status][Discuss] Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 123456789123456789. 有时,Alice 会选择一条从 s 到 t 的路径,在这条路径上的每一个点上都添加一个数字.对于路径上

【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)

Time:2016.05.10 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 李超线段树 一开始听faebdc讲,并没有听的很懂ww 后来找到良心博文啊有木有 折越 首先可以把修改转换一下,因为那个dis非常不爽.显然s~t的路径有s~lca和lca~t组成.令d[x]表示x的深度,对于s~lca上面的点,修改的值相当于a*(d[s]-d[x])+b=-a*d[x]+(b-a*d[s]),lca~t上面的点的值相当于a*(d[s]+d[x]-2*d[lca])+b=a*d[x

李超线段树

李超线段树可以维护两两间至多有一个交点的函数覆盖,单点求极值问题. codechef NOV17 POLY 给定n个形如yi(x)=$a0+a1^x+a2x^2+a3x^3$的函数以及q个询问.每个询问给定整数t,你需要求出使得yi(t)最小化的函数yi. Lemma: Polynomial $y=x^3+ax^2+bx+c$ has at most one root greater than $k=\sqrt{\max(|b|,|c|)}+2$. Proof: Let $u\geq v >k\

[CodeChef - STREETTA] The Street 李超线段树

大致题意: 给出两个序列A,B,A初始为负无穷,B初始为0,有三种操作 1.在A上区间[u,v]上加一个等差数列,取与原本A序列的最大值. 2.在B上区间[u,v]上加一个等差数列. 3.给出一个点X,询问A[X]+B[X]的值. 学习一个李超线段树就ojbk了,对于每次加入的等差数列,可以转化为y=a*i+b的一条线段,用李超线段树维护所有线段 所覆盖的区间即可.数据范围比较大,线段树可以动态开点,也可以离散化. 1 #include<cstdio> 2 #include<iostre

HDU6447 YJJ&#39;s Salesman 2018中国大学生程序设计竞赛 - 网络选拔赛1010 离散化+线段树+DP

YJJ's Salesman Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 253    Accepted Submission(s): 62 Problem Description YJJ is a salesman who has traveled through western country. YJJ is always on

[HDU 6447][YJJ&#39;s Salesman][2018CCPC网络选拔赛 1010][离散化+线段树+DP]

链接: http://acm.hdu.edu.cn/showproblem.php?pid=6447 题意: 左上角(0,0),右下角(10^9,10^9)的网格,其中有n(1<=n<=10^5)个方格内有权值. 一次只能沿右,下,右下三个方向走一个格子,只有沿右下方向走到格子里才可以获得权值. 问从(0,0)到(10^9,10^9)的路径最大权值是多少. 思路: 网格路径权值问题,第一感考虑DP,x从上往下,y从左往右刷表,状态转移方程为dp[i][j]=max(dp[i-1][j],dp[