施咒的最大总伤害
因为咒语释放的顺序无关,考虑按伤害值从小到大释放咒语。把伤害相同的咒语看成一种,设 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;
}
};