树状数组维护DP——前缀最大值

树状数组维护DP

例题1

例题1

给定一个 n × m n\times m n×m 的网格图,设 ( i , j ) (i,j) (i,j) 表示从上往下数第 i i i 行与从左往右数第 j j j 列的交点。有一辆公交车在 ( 1 , 1 ) (1,1) (1,1) 处,它每次只能向往右或下行驶,目的地是 ( n , m ) (n, m) (n,m),给定 k k k 个公交站点,每个站点 ( x i , y i ) (x_i,y_i) (xi,yi) 有 p i p_i pi 位乘客,请问公交车至多能载多少名乘客到达终点?

数据范围: 1 ≤ n , m ≤ 1 0 9 , 1 ≤ k ≤ 1 0 5 1\le n,m\le 10^9,1\le k\le 10^5 1≤n,m≤109,1≤k≤105, 1 ≤ x i ≤ n 1\le x_i\le n 1≤xi≤n, 1 ≤ y i ≤ m 1\le y_i\le m 1≤yi≤m, 1 ≤ p i ≤ 1 0 6 1\le p_i\le 10^6 1≤pi≤106。

题解

如果 n , m n,m n,m 较小,我们可以利用动态规划求解: d p i j = max ⁡ ( d p i − 1 j , d p i j − 1 ) dpij=\max(dpi-1j,dpij-1) dpij=max(dpi−1j,dpij−1),时间复杂度是 O ( n m ) O(nm) O(nm)。

但 n , m n,m n,m 的规模较大,我们并不能遍历所有的格子进行转移,对于一个车站 ( i , j ) (i,j) (i,j),如果我们要求出从 ( 1 , 1 ) (1,1) (1,1) 到达 ( i , j ) (i,j) (i,j) 的最大乘客数量,应该从 ( i , j ) (i,j) (i,j) 的左上角里的最大值进行转移。

对所有的公交站点坐标 ( x , y ) (x,y) (x,y) 进行离散化,且将所有的公交站点按 y y y 从小到大排序, y y y 相同则按 x x x 从小到大排序。

设 d p x dpx dpx 表示大巴车从起点行驶到离散后的第 x x x 行能装载的最大乘客数量。

对于当前的 ( x i , y i ) (x_i,y_i) (xi,yi),所有在其左上角的 ( x j , y j ) (x_j,y_j) (xj,yj) 均已出现,所以 d p x i dpx_i dpxi 只需从 d p 1 ∼ x i dp1\\sim x_i dp1∼xi 处挑选最大值进行转移。

而树状数组能够维护支持单点修改的前缀最大值,总时间复杂度为 O ( k log ⁡ k ) O(k\log k) O(klogk)。

值得注意的是,离散后的 x x x 值可能是 0 0 0,但是树状数组只能维护从 1 1 1 开始的前缀,所以需要将离散后的值偏移到从 1 1 1 开始。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define int long long
#define endl '\n'
#define PII pair<int,int>
#define INF 1e18

struct Fenwick {
    int n;
    vector <int> tree;

    Fenwick(int _n) {
        n = _n;
        tree.assign(n + 2, 0);
    }

    int getPrefixMax(int x) {
        int maxn = 0;

        while (x) {
            maxn = max(maxn, tree[x]);
            x = x - lowbit(x);
        }
        return maxn;
    }

    void add (int x, int val) {
        while (x <= n) {
            tree[x] = max(tree[x], val);
            x += lowbit(x);
        }
    }

    static inline int lowbit(int x) {
        return x & (-x);
    }
};

struct node {
    int x, y;
    int p;
};

void lsh (vector <int> & a) {
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
}

void slove () {
    int n, m, k;
    cin >> n >> m >> k;

    vector <node> v;

    vector <int> xPos, yPos;
    for (int i = 1; i <= k; i++) {
        int x, y, p;
        cin >> x >> y >> p;
        xPos.push_back(x);
        yPos.push_back(y);
        v.push_back(node{x, y, p});
    }

    sort (v.begin(), v.end(), [](node A, node B) {
        if (A.y != B.y) return A.y < B.y;
        return A.x < B.x;
    });

    lsh(xPos);
    lsh(yPos);

    Fenwick tt(xPos.size());
    vector <int> dp(xPos.size(), 0);

    for (int i = 0; i < v.size(); i++) {
        int x;
        x = lower_bound(xPos.begin(), xPos.end(), v[i].x) - xPos.begin();
        int maxn;
        maxn = tt.getPrefixMax(x + 1);
        dp[x] = maxn + v[i].p;
        tt.add(x + 1, dp[x]);
    }

    int ans = 0;
    for (int i = 0; i < xPos.size(); i++) {
        ans = max(ans, dp[i]);
    }
    cout << ans << endl;
}

signed main () {
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    slove();
}
相关推荐
yuan199978 分钟前
欧拉梁静力与屈曲计算的 MATLAB 实现(有限差分法 + 解析解)
开发语言·算法·matlab
FL162386312927 分钟前
[cmake]基于C++使用纯opencv部署ppocrv5v6的onnx模型
开发语言·c++·opencv
玖玥拾32 分钟前
C/C++ 数据结构(六)链表迭代器与底层
c语言·数据结构·c++·链表·stl库
牛油果子哥q43 分钟前
AVL平衡树与红黑树深度精讲对比,平衡因子、四大旋转原理、着色规则、平衡策略、性能差异与面试手撕全解
数据结构·c++·面试
汉克老师1 小时前
GESP7级C++考试语法知识(二、指数函数(3、综合练习)
c++·算法·数学建模·指数函数·gesp7级·复利
C++ 老炮儿的技术栈1 小时前
Ubuntu root账号自动登陆
linux·运维·服务器·c语言·c++·ubuntu·visual studio
林间码客2 小时前
04 ROC曲线与AUC:从零开始手动计算
大数据·人工智能·算法
Irissgwe2 小时前
map/set/multimap/multiset 的底层逻辑与实现
数据结构·c++·算法·二叉树·stl·c·红黑树
IronMurphy2 小时前
【算法五十八】23. 合并 K 个升序链表
数据结构·算法·链表
思茂信息2 小时前
CST软件基于液态金属开关的方向图可重构天线
服务器·算法·重构·cst·仿真软件·电磁仿真