LeetCode402周赛T3(Dp) +T4(树状数组)

施咒的最大总伤害
因为咒语释放的顺序无关,考虑按伤害值从小到大释放咒语。把伤害相同的咒语看成一种,设 f[i] 为针对前i个数的所取得的最值

class Solution {
public:
    long long maximumTotalDamage(vector<int>& power) {
        // 统计每种咒语有几个
        map<int, int> mp;
        for (int x : power) mp[x]++;
        typedef pair<int, int> pii;
        // vec 里按伤害从小到大保存每种咒语的伤害和数量
        vector<pii> vec;
        vec.push_back(pii(-1e9, 0));
        for (auto &p : mp) vec.push_back(p);

        int n = vec.size();
        long long f[n];
        f[0] = 0;

        long long mx = 0;
        for (int i = 1, j = 1; i < n; i++) {
            // 维护单调指针 j
            while (j < i && vec[j].first < vec[i].first - 2) {
                mx = max(mx, f[j]);
                j++;
            }
            // 套 DP 方程
            f[i] = mx + 1LL * vec[i].first * vec[i].second;
        }

        long long ans = 0;
        for (int i = 1; i < n; i++) ans = max(ans, f[i]);
        return ans;
    }

数组中的峰值
单点修改,区间查询。用树状数组或者线段树
这里用树状数组,每次flag[i]=1代表山峰,flag[i]=0代表山谷。对于查询[L,R]那么只需要查询区间之和即可。
重点是如何更新,对于每个询问,需要更新i-1,i,i+1三个点的山峰山谷情况,需要注意的是,add函数在统计区间和的,如果需要单点修改为别的值,需要先减去原来的值

class Solution {
public:
    vector<int> countOfPeaks(vector<int>& nums, vector<vector<int>>& queries) {
        int n = nums.size();

        // 树状数组模板开始

        int tree[n];
        memset(tree, 0, sizeof(tree));

        auto lb = [&](int x) { return x & (-x); };

        auto add = [&](int pos, int val) {
            for (; pos < n; pos += lb(pos)) tree[pos] += val;
        };

        auto query = [&](int pos) {
            if (pos <= 0) return 0;
            int ret = 0;
            for (; pos; pos -= lb(pos)) ret += tree[pos];
            return ret;
        };

        // 树状数组模板结束
        
        int flag[n];
        memset(flag, 0, sizeof(flag));

        // 重新计算下标 i 是否为峰值
        auto recalc = [&](int i) {
            if (i <= 0 || i >= n - 1) return;
            add(i, -flag[i]);
            flag[i] = (nums[i] > nums[i - 1] && nums[i] > nums[i + 1] ? 1 : 0);
            add(i, flag[i]);
        };

        // 初始化每个位置的峰值情况
        for (int i = 1; i + 1 < n; i++) recalc(i);

        vector<int> ans;
        for (auto &qry : queries) {
            if (qry[0] == 1) {
                // 根据题目要求,子数组的首尾元素均不是峰值,因此询问区间的头尾需要各缩小 1
                int L = qry[1] + 1, R = qry[2] - 1;
                if (L > R) ans.push_back(0);
                else ans.push_back(query(R) - query(L - 1));
            } else {
                int idx = qry[1], val = qry[2];
                nums[idx] = val;
                // 直接重算三个元素的峰值情况
                for (int d = -1; d <= 1; d++) recalc(idx + d);
            }
        }
        return ans;
    }
};

线段树写法

const int N = 1e5 + 10;

class Solution {
public:
    struct node
    {
        int l, r;
        int v;
    }tr[N * 4];
    void pushup(int u )
    {
        tr[u].v = tr[u<<1].v + tr[u<<1|1].v ;
    }
    void build(int u, int l, int r)
    {
        tr[u] = {l, r};
        if(l == r)return;
        int mid = l + r >>1;
        build(u<<1, l, mid);
        build(u<<1 | 1, mid+1, r);
    }

    int query(int u, int l, int r)
    {
        if(l <= tr[u].l &&   tr[u].r <=r )return tr[u].v;
        int mid = tr[u].l + tr[u].r >>1;
        int res = 0;
        if(l <= mid) res = query(u<<1, l, r);
        if(r > mid) res += query(u<<1 | 1, l, r);
        return res;
    }

    void modify(int u, int k, int x)
    {
        if(tr[u].l == tr[u].r)tr[u].v = x;
        else
        {
            int mid = tr[u].l + tr[u].r >>1;
            if(k <= mid)modify(u<<1, k ,x);
            else modify(u<<1 | 1, k, x);
            pushup(u);
        }
    }
    vector<int> countOfPeaks(vector<int>& nums, vector<vector<int>>& queries) {
        
        int n = nums.size();
        vector<int> res;
        build(1, 1, n);
        int cnt = 0;
        for(int i = 1; i < n - 1; i ++)
        {
            if(nums[i] > nums[i-1] && nums[i] > nums[i + 1])
            {
                modify(1, i + 1, 1);
            }
        }
        for(auto c : queries)
        {
            int x = c[1], y = c[2];
            if(c[0] == 1)
            {
                x ++;y ++;
                int l = x + 1, r = y - 1;
              
                if(l > r)res.push_back(0);
                else res.push_back(query(1,l ,r ));
            }
            else
            {
                nums[x] = y;
                for(int i = x - 1; i <= x + 1; i ++)
                {
                     if (i <= 0 || i >= n - 1) continue;;
                     if(nums[i] > nums[i-1] && nums[i] > nums[i + 1])modify(1, i + 1, 1);
                     else modify(1, i + 1, 0);
                }
            }
        }
        return res;
    }
};
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值