DP方程十分简单,考虑前对后贡献即可。

\(f_i = \min_{l_i \leq j < i} \left\{ f_j + \left(\max_{j < k \leq i} \left\{t_k\right\}\right) \times \left(\sum_{k=i+1}^n w_k \right)\right\}\)

\(O\left(n^2\right)\) 显然。\(w\) 后缀和递减,\(\max t\) 向左单调增,显然单调性优化。

使用单调栈将 \(\max t\) 分成几段,然后查询需要维护区间凸壳。因为单调栈需要支持末尾插入,末尾删除,区间查询凸壳,十分麻烦。

做法很多,学了一下姿势。(当然不会去写动态凸包的啊)

对于一般的支持末尾插入删除区间询问凸壳的问题,有如下做法:

(因为很多时候题目性质不同,就不仔细分析复杂度了)

树形法

把单调栈变成树形结构,弹出是跳fa,插入是跳儿子,那么查询是树上的链。

点分治

把询问拆成经过重心的两条链,分别在各自子树中查询,并且启发式凸包合并。

但是这里貌似是有根树分治,那么直接上面的链建单调队列,贡献到下面就好了。

树剖

直接树剖,对于每条链用线段树维护。

平衡树

考虑插入节点时删除的节点是一个区间,那么平衡树二分这个区间,删除的时候绑定到新节点上。

这样在新节点被删除的时候,把原区间归还即可。

二进制分组

延迟重构 normal trick?

用线段树维护二进制分组很方便,删除时到根打标记,保持每个组仅有右链有tag。

查询时如果有tag就递归俩儿子。

如果插入时可以合并组,如果子树有标记,递归重构即可。

势能分析一波,删除重构不会太多。

时间戳线段树

不要删除,直接区间放点好了。


我写了发二进制分组。合并复杂度线性,查询因为横坐标单调,所以使用单调队列。时空一个log。

画图时算叉积把方向画错了,导致打错个符号,拍巨久才拍出来……/px

#include <bits/stdc++.h>

const int MAXN = 100010;
typedef long long LL;
typedef long double LD;
const LL INFL = 0x3f3f3f3f3f3f3f3fLL;
typedef std::vector<int> VI;
void getmin(LL & x, LL y) { x > y ? x = y : 0; }
int n, ls[MAXN], ts[MAXN], ws[MAXN];
LL suc[MAXN], dp[MAXN];
int st[MAXN], top, vs[MAXN];
namespace ch {
    int tag[MAXN << 2], at[MAXN], sz[MAXN << 2];
    void build(int u, int l, int r) {
        sz[u] = r - l + 1;
        if (l == r) return (void) (at[l] = u);
        int mid = l + r >> 1;
        build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
    }
    LL ks[MAXN], bs[MAXN]; int bak;
    bool chk(int a, int b, LL at) {
        return (LD) ks[a] * at + bs[a] >= (LD) ks[b] * at + bs[b];
    }
    bool cross(LL x1, LL y1, LL x2, LL y2) {
        return (LD) x1 * y2 - (LD) x2 * y1 >= -1e-10;
    }
    bool cmp(int a, int b, int c) {
        return cross(ks[b] - ks[a], bs[b] - bs[a], ks[c] - ks[a], bs[c] - bs[a]);
    }
    struct monoq {
        VI que; int b, e;
        void set(int at) { que.clear(); que.push_back(at); b = e = 0; }
        void merge(const monoq & a, const monoq & b) {
            int ta = a.b, tb = b.b;
            static int st[MAXN], top, now; top = 0;
            for (int T = a.e - ta + b.e - tb + 2; T; --T) {
                if (ta <= a.e && (tb > b.e || ks[a.que[ta]] >= ks[b.que[tb]]))
                    now = a.que[ta++];
                else now = b.que[tb++];
                while (top > 1 && cmp(st[top - 1], st[top], now)) --top;
                st[++top] = now;
            }
            que.assign(st + 1, st + top + 1);
            this -> b = 0, e = top - 1;
        }
        LL qry(LL at) {
            while (e - b + 1 >= 2 && chk(que[b], que[b + 1], at)) ++b;
            return ks[que[b]] * at + bs[que[b]];
        }
    } tree[MAXN << 2];
    LL exqry(int u, LL at) {
        if (!tag[u])
            return std::min(exqry(u << 1, at), exqry(u << 1 | 1, at));
        return tree[u].qry(at);
    }
    LL query(int u, int l, int r, int L, int R, LL at) {
        if (L <= l && r <= R) return exqry(u, at);
        int mid = l + r >> 1; LL res = INFL;
        if (L <= mid) res = query(u << 1, l, mid, L, R, at);
        if (mid < R) res = std::min(res, query(u << 1 | 1, mid + 1, r, L, R, at));
        return res;
    }
    LL qry(LL at, int L) { return query(1, 1, n, L, bak, at); }
    void rebuild(int u) {
        if (tag[u]) return ;
        if (!sz[u << 1] && !sz[u << 1 | 1]) {
            rebuild(u << 1); rebuild(u << 1 | 1);
            tag[u] = true;
            tree[u].merge(tree[u << 1], tree[u << 1 | 1]);
        }
    }
    void push_back(LL k, LL b) {
        int u = at[++bak];
        ks[bak] = k, bs[bak] = b;
        tree[u].set(bak); tag[u] = true;
        while (u) {
            --sz[u], u >>= 1;
            if (u) rebuild(u);
        }
    }
    void pop_back() {
        int u = at[bak--];
        while (u) ++sz[u], tag[u] = false, u >>= 1;
    }
}
struct segmenttree {
    LL tree[MAXN << 2];
    void mdf(int u, int l, int r, int tar, LL v) {
        if (l == r) return (void) (tree[u] = v);
        int mid = l + r >> 1;
        if (tar <= mid) mdf(u << 1, l, mid, tar, v);
        else mdf(u << 1 | 1, mid + 1, r, tar, v);
        tree[u] = std::min(tree[u << 1], tree[u << 1 | 1]);
    }
    LL qry(int u, int l, int r, int L, int R) {
        if (L <= l && r <= R) return tree[u];
        int mid = l + r >> 1; LL res = INFL;
        if (L <= mid) res = qry(u << 1, l, mid, L, R);
        if (mid < R) res = std::min(res, qry(u << 1 | 1, mid + 1, r, L, R));
        return res;
    }
    LL qry(int l, int r) { return l > r ? INFL : qry(1, 0, n, l, r); }
} seg;
int main() {
    std::ios_base::sync_with_stdio(false), std::cin.tie(0);
    std::cin >> n;
    ch::build(1, 1, n);
    for (int i = 1; i <= n; ++i)
        std::cin >> ls[i] >> ts[i] >> ws[i];
    for (int i = n; i; --i) suc[i] = suc[i + 1] + ws[i];
    memset(dp, 0x3f, n + 1 << 3);
    seg.mdf(1, 0, n, 0, dp[0] = 0);
    for (int i = 1; i <= n; ++i) {
        while (top && vs[top] <= ts[i])
            --top, ch::pop_back();
        int l = st[top] + 1;
        st[++top] = i, vs[top] = ts[i];
        ch::push_back(ts[i], seg.qry(l - 1, i - 1));
        int tl = std::lower_bound(st + 1, st + 1 + top, ls[i]) - st;
        LL t = seg.qry(ls[i], st[tl] - 1);
        getmin(dp[i], t + suc[i + 1] * vs[tl]);
        if (tl < top) getmin(dp[i], ch::qry(suc[i + 1], tl + 1));
        seg.mdf(1, 0, n, i, dp[i]);
    }
    std::cout << dp[n] << std::endl;
    return 0;
}

【集训队作业2018】line的更多相关文章

  1. UOJ #449. 【集训队作业2018】喂鸽子

    UOJ #449. [集训队作业2018]喂鸽子 小Z是养鸽子的人.一天,小Z给鸽子们喂玉米吃.一共有n只鸽子,小Z每秒会等概率选择一只鸽子并给他一粒玉米.一只鸽子饱了当且仅当它吃了的玉米粒数量\(≥ ...

  2. [UOJ422][集训队作业2018]小Z的礼物——轮廓线DP+min-max容斥

    题目链接: [集训队作业2018]小Z的礼物 题目要求的就是最后一个喜欢的物品的期望得到时间. 根据$min-max$容斥可以知道$E(max(S))=\sum\limits_{T\subseteq ...

  3. 【UOJ#450】【集训队作业2018】复读机(生成函数,单位根反演)

    [UOJ#450][集训队作业2018]复读机(生成函数,单位根反演) 题面 UOJ 题解 似乎是\(\mbox{Anson}\)爷的题. \(d=1\)的时候,随便怎么都行,答案就是\(k^n\). ...

  4. 【UOJ#422】【集训队作业2018】小Z的礼物(min-max容斥,轮廓线dp)

    [UOJ#422][集训队作业2018]小Z的礼物(min-max容斥,轮廓线dp) 题面 UOJ 题解 毒瘤xzy,怎么能搬这种题当做WC模拟题QwQ 一开始开错题了,根本就不会做. 后来发现是每次 ...

  5. UOJ#418. 【集训队作业2018】三角形

    #418. [集训队作业2018]三角形 和三角形没有关系 只要知道儿子放置的顺序,就可以直接模拟了 记录历史最大值 用一个pair(a,b):之后加上a个,期间最大值为增加b个 合并? A1+A2= ...

  6. 2019.2.25 模拟赛T1【集训队作业2018】小Z的礼物

    T1: [集训队作业2018]小Z的礼物 我们发现我们要求的是覆盖所有集合里的元素的期望时间. 设\(t_{i,j}\)表示第一次覆盖第i行第j列的格子的时间,我们要求的是\(max\{ALL\}\) ...

  7. [集训队作业2018]蜀道难——TopTree+贪心+树链剖分+链分治+树形DP

    题目链接: [集训队作业2018]蜀道难 题目大意:给出一棵$n$个节点的树,要求给每个点赋一个$1\sim n$之内的权值使所有点的权值是$1\sim n$的一个排列,定义一条边的权值为两端点权值差 ...

  8. UOJ#422. 【集训队作业2018】小Z的礼物

    #422. [集训队作业2018]小Z的礼物 min-max容斥 转化为每个集合最早被染色的期望时间 如果有x个选择可以染色,那么期望时间就是((n-1)*m+(m-1)*n))/x 但是x会变,中途 ...

  9. UOJ#428. 【集训队作业2018】普通的计数题

    #428. [集训队作业2018]普通的计数题 模型转化好题 所以变成统计有标号合法的树的个数. 合法限制: 1.根标号比子树都大 2.如果儿子全是叶子,数量B中有 3.如果存在一个儿子不是叶子,数量 ...

  10. uoj450 【集训队作业2018】复读机(生成函数,单位根反演)

    uoj450 [集训队作业2018]复读机(生成函数,单位根反演) uoj 题解时间 首先直接搞出单个复读机的生成函数 $ \sum\limits_{ i = 0 }^{ k } [ d | i ] ...

随机推荐

  1. luogu P1646 [国家集训队]happiness (最小割)

    高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友.这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科 ...

  2. StoneTab标签页CAD插件 3.2.5

    //////////////////////////////////////////////////////////////////////////////////////////////////// ...

  3. ECMAScript中的原型继承

    //ECMAScript中的原型继承//ECMAScript中的继承主要是依靠原型链实现的.(关于原型链的介绍,详见<高三>6.3.1章节 P162) //本文示例主要为了说明SubTyp ...

  4. T4模板生成文件要点记录

    可以使用 $(variableName) 语法引用 Visual Studio 或 MSBuild 变量(如 $(SolutionDir)),以及使用 %VariableName% 来引用环境变量.介 ...

  5. linux Linux入门

    Linux入门 Linux学习什么? 常用命令(背会) 软件安装(熟练) 服务端的架构(开开眼界) Linux如何学习? 不要问那么多为什么,后面你就懒得问了 先尝试理解一下,不行就背下来 一个知识点 ...

  6. shake.js实现微信摇一摇功能

    项目要求实现点击摇一摇图片,图片摇一摇,并且摇一摇手机,图片也要摇一摇. 关于用js怎样实现摇一摇手机图片摇一摇,我在网络上找了一些方法,真正有用的是shake.js. 接下来,上shake.js源码 ...

  7. sql server 查看表中某一字段的排序规则

    SELECT o.name,o.object_id,c.name,c.column_id,c.collation_name   FROM sys.columns c      JOIN sys.obj ...

  8. Java虚拟机内存基础、垃圾收集算法及JVM优化

    1 JVM 简单结构图   1.1 类加载子系统与方法区 类加载子系统负责从文件系统或者网络中加载 Class 信息,加载的类信息存放于一块称 为方法区的内存空间.除了类的信息外,方法区中可能还会存放 ...

  9. Django 开发相关知识 整理

    前言 前端ajax HTTP请求头 ajax上传文件 jsonp跨域 URL 设计 分发 url参数编码 反向生成url 视图 request对象 POST url信息 视图返回值 HttpRespo ...

  10. 08_Hive中的各种Join操作

    1.关于hive中的各种join Hive中有许多的Join操作,例如:LEFT.RIGHT和FULL OUTER JOIN,INNER JOIN,LEFT SEMI JOIN等: 1.1.准备两组数 ...