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. java包装类的缓存机制(转)

    出处: java包装类的缓存机制 java 包装类的缓存机制,是在Java 5中引入的一个有助于节省内存.提高性能的功能,只有在自动装箱时有效 Integer包装类 举个栗子: Integer a = ...

  2. java 缓存

    外存: 也就是我们经常说的(CDEF盘的大小)外储存器是指除计算机内存及CPU缓存以外的储存器,此类储存器一般断电后仍然能保存数据.常见的外存储器有硬盘.软盘.光盘.U盘等,一般的软件都是安装在外存中 ...

  3. 把.exe的格式的运行程序加到电脑本地服务的办法(本文来源于百度)

    Instsrv.exe(可以给系统安装和删除服务) Srvany.exe(可以让程序以服务的方式运行) 方法/步骤     要实现这个功能要用到微软提供的两个小工具“instsrv.exe”和“srv ...

  4. Linux学习(四)-Linux常用命令

    1.运行级别类 1.1运行级别说明: 0:关机 1:单用户[可用于找回丢失密码] 2:多用户状态没有网络服务 3:多用户状态有网络服务 4:系统未使用保留给用户 5:图形界面 6:系统重启 常用运行级 ...

  5. 安卓开发之ListView入门

    <!--这个地方最好用match_parent 这样效率高--> <ListView android:layout_width="match_parent" an ...

  6. C++ STL 之 string

    #include <iostream> #include <string> using namespace std; // 初始化 void test01() { string ...

  7. excel中的更新链接

    表格每次打开都提示是否更新连接 在 [  数据 -->   编辑链接  ]  中也看到了这个连接 学着网上说的查找方式,去查找路径中包含的文字,文件名中包含的名字,都定位不到这个用了链接的单元格 ...

  8. dict 字典 函数值应用

    函数 说明 D代表字典对象   D.clear() 清空字典 D.pop(key) 移除键,同时返回此键所对应的值 D.copy() 返回字典D的副本,只复制一层(浅拷贝) D.update(D2) ...

  9. JavaSpring【五、AOP基础】

    概念: AOP--面向切面编程,通过预编译/动态代理实现程序功能的统一维护 主要功能是:日志.性能统计.安全控制.事务处理.异常处理 实现方式 预编译--AspectJ 动态代理--SpringAOP ...

  10. Mysql(七):视图、触发器、事务、存储过程、函数

    一 视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,可以将该结果集当做表来使用. 使用视图我们可以把查询过程中的 ...