最大和查询

给你两个长度为 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;
    }
};
posted @ 2023-06-12 03:17  失控D大白兔  阅读(4)  评论(0编辑  收藏  举报