最大和查询
给你两个长度为 n 、下标从 0 开始的整数数组 nums1 和 nums2
另给你一个下标从 1 开始的二维数组 queries ,其中 queries[i] = [xi, yi]
对于第 i 个查询,在所有满足 nums1[j] >= xi 且 nums2[j] >= yi 的下标 j (0 <= j < n) 中
找出 nums1[j] + nums2[j] 的 最大值 ,如果不存在满足条件的 j 则返回 -1 。
1. 排序+单调栈+二分
贪心构造使栈中剩余元素满足条件
class Solution {
public:
vector<int> maximumSumQueries(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
int n = nums1.size();int m = queries.size();
vector<int>ans(m,-1);
vector<pair<int,int>>p(n);
for(int i = 0; i < n; i++) //合并成一个数组
p[i] = {nums1[i], nums2[i]};
for(int i = 0; i < m; i++) //添加一个维度记录原始位置
queries[i].push_back(i);
sort(p.begin(), p.end(), [&](const auto &a, const auto &b){//第一维降序
return a.first > b.first;});
sort(queries.begin(),queries.end(),[&](const auto &a, const auto &b){//第一维降序
return a[0] > b[0];});
vector<pair<int,int>> mono;//单调栈
int j = 0;
for(int i = 0; i< m; i++) {
while(j < n && p[j].first >= queries[i][0]) {
while(mono.size() > 0 && mono.back().second <= p[j].first + p[j].second)
mono.pop_back();
if(mono.size() == 0 || mono.back().first < p[j].second)
mono.push_back({p[j].second,p[j].first+p[j].second});
j++;
}
if(mono.empty())
ans[queries[i][2]] = -1;
else {
auto it = lower_bound(mono.begin(), mono.end(), queries[i][1],[&](const pair<int, int>&a, int value){
return a.first < value;
});
if(it != mono.end()) {
int index = it-mono.begin();
ans[queries[i][2]] = mono[index].second;
}else
ans[queries[i][2]] = -1;
}
}
return ans;
}
};
2. 树状数组 + 离散化(二分)(查询点和插入点不合并)
典型的偏序问题
固定一维的遍历顺序,对另一维插入和查询
class Solution {
public:
vector<int> tree;
int n; int m;
vector<int> maximumSumQueries(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
//目标是在num1和num2上找满足条件的最大值
//设queries的限制条件分别是x和y
n = nums1.size(); m =queries.size();
vector<vector<int>> keys;
for(int i=0;i<n;i++)
keys.push_back({nums1[i],nums2[i]});
//把查询点和实际点放在一起,进行离散树状数组查询
//因为要查大的,所以对于限制条件x,由大到小查,保证插入点第一维满足条件,找大于y的第二维即可
//每次查询时就能知道已经有多少更大值插入
//这里使用二分完成值的离散化(即值和下标对应),不用将两类数组合并同时添加更多标识位
vector<int> copy(m+n);
int index = 0;
for(auto&k:keys)
copy[index++] = k[1];
for(auto&q:queries)
copy[index++] = q[1];
sort(copy.begin(),copy.end()); //对第二维进行升序排列,为了二分查找,快速完成值和下标的对应
for(auto&k:keys)
k[1] = lower_bound(copy.begin(), copy.end(), k[1]) - copy.begin()+1;//第二维存储下标,相同值用同一个下标
for(auto& q: queries)
q[1] = lower_bound(copy.begin(), copy.end(), q[1]) - copy.begin()+1;//第二维存储下标
vector<int> ids(m);//查询点按第一维降序排列
iota(ids.begin(),ids.end(),0);
sort(ids.begin(), ids.end(), [&](int& a, int& b){
return queries[a][0] > queries[b][0];});
//同时把实际点也按第一维降序排列
sort(keys.begin(), keys.end(), [&](vector<int>& a, vector<int>& b){return a[0] > b[0];});
int ptr = 0; //实际点下标,遍历时第一维降序
vector<int> res(m,-1);
tree.resize(m+n+1,-1);//初始化值为-1
for(auto&id:ids){//第一维降序遍历查询点
while(ptr<n&&keys[ptr][0]>=queries[id][0]){ //单点修改第一维更大的实际点
updata(keys[ptr][1],keys[ptr][0]+copy[keys[ptr][1]-1]);
ptr++;
}
//插入查询点查询
res[id] = query(queries[id][1]);
}
return res;
}
//树状数组模板
int lowbit(int x){//求二进制化最后一位的值
return x&(-x);
}
void updata(int i,int k){ //在i位置更新k,O(logn)复杂度单点修改
while(i>0){//更新子树上所有值
tree[i] = max(tree[i],k);//更新为最大值
i-=lowbit(i);//继续更新前面区间
}
}
int query(int i){ //求数组i及后面最大值
int res=-1;
while(i<=n+m){//O(logn)求前缀和
res = max(res,tree[i]);
i+=lowbit(i);//移动到后面区间
}
return res;
}
};
3. 树状数组 + 离散化(哈希)(查询点和插入点不合并)
class Solution {
public:
vector<int> tree;
int n; int m;
vector<int> maximumSumQueries(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
n = nums1.size(); m =queries.size();
vector<vector<int>> keys;
for(int i=0;i<n;i++)
keys.push_back({nums1[i],nums2[i]});
int index = 0;
map<int,int> m1;//值和下标的对应
map<int,int> m2;//下标和值的对应,因为还要用到值
for(auto&k:keys)
m1[k[1]]++;
for(auto&q:queries)
m1[q[1]]++;
for(auto& [val,idx]:m1){//使用哈希离散化下标
idx = index++;
m2[idx] = val;
}
for(auto&k:keys)
k[1] = m1[k[1]]+1;//第二维存储下标,相同值用同一个下标
for(auto& q: queries)
q[1] = m1[q[1]]+1;//第二维存储下标
vector<int> ids(m);//查询点按第一维降序排列
iota(ids.begin(),ids.end(),0);
sort(ids.begin(), ids.end(), [&](int& a, int& b){
return queries[a][0] > queries[b][0];});
//同时把实际点也按第一维降序排列
sort(keys.begin(), keys.end(), [&](vector<int>& a, vector<int>& b){return a[0] > b[0];});
int ptr = 0; //实际点下标,遍历时第一维降序
vector<int> res(m,-1);
tree.resize(m+n+1,-1);//初始化值为-1
for(auto&id:ids){//第一维降序遍历查询点
while(ptr<n&&keys[ptr][0]>=queries[id][0]){ //单点修改第一维更大的实际点
updata(keys[ptr][1],keys[ptr][0]+m2[keys[ptr][1]-1]);
ptr++;
}
//插入查询点查询
res[id] = query(queries[id][1]);
}
return res;
}
//树状数组模板
int lowbit(int x){//求二进制化最后一位的值
return x&(-x);
}
void updata(int i,int k){ //在i位置更新k,O(logn)复杂度单点修改
while(i>0){//更新子树上所有值
tree[i] = max(tree[i],k);//更新为最大值
i-=lowbit(i);//继续更新前面区间
}
}
int query(int i){ //求数组i及后面最大值
int res=-1;
while(i<=n+m){//O(logn)求前缀和
res = max(res,tree[i]);
i+=lowbit(i);//移动到后面区间
}
return res;
}
};
4. 树状数组+离散化(插入点查询点合并)
思路跟方法23一致
按第一维降序顺序遍历访问
第二维进行离散化处理
我们将查询点和插入点放入一个数组中
按第一维降序排,然后顺序访问,对第二维进行插入查询
一个元素要有第一维、第二维和点类型判断位
同时排序时要保证插入点在查询点前面即可
class Solution {
public:
vector<int> tree;
int n; int m;
vector<int> maximumSumQueries(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
n = nums1.size(); m =queries.size();
vector<vector<int>> keys;
for(int i=0;i<n;i++)
keys.push_back({nums1[i],nums2[i],-1});//-1表示插入点
for(int i=0;i<m;i++)
keys.push_back({queries[i][0],queries[i][1],i});//i表示查询点,同时记录原来的位置,用于输出结果
int index = 0;
map<int,int> m1;//值和下标的对应
map<int,int> m2;//下标和值的对应,因为还要用到值
for(auto&k:keys) //因为相同值要赋相同下标,这里不用排序处理,使用哈希处理更方便
m1[k[1]]++;
for(auto& [val,idx]:m1){//使用哈希离散化下标
idx = index++;
m2[idx] = val; //顺便存储一下第二维的值
}
for(auto&k:keys)
k[1] = m1[k[1]]+1;//第二维存储下标,相同值用同一个下标
//按第一维降序排列
sort(keys.begin(), keys.end(), [&](vector<int>& a, vector<int>& b){
if(a[0]==b[0]) return a[2]<b[2];//插入点在前面
return a[0] > b[0];});
vector<int> res(m,-1);
tree.resize(m+n+1,-1);//初始化值为-1
for(int i=0;i<keys.size();i++){
if(keys[i][2]==-1)//插入
updata(keys[i][1],keys[i][0]+m2[keys[i][1]-1]);
else
res[keys[i][2]] = query(keys[i][1]);
}
return res;
}
//树状数组模板
int lowbit(int x){//求二进制化最后一位的值
return x&(-x);
}
void updata(int i,int k){ //在i位置更新k,O(logn)复杂度单点修改
while(i>0){//更新子树上所有值
tree[i] = max(tree[i],k);//更新为最大值
i-=lowbit(i);//继续更新前面区间
}
}
int query(int i){ //求数组i及后面最大值
int res=-1;
while(i<=n+m){//O(logn)求前缀和
res = max(res,tree[i]);
i+=lowbit(i);//移动到后面区间
}
return res;
}
};