CF-514-D-单调队列

514-D 题目大意

给定\(n\)个人,每个人有\(m\)个属性,第\(i\)个人的第\(j\)个属性值为\(a[i][j]\)

最多可以执行\(k\)次操作,每次操作选定一个属性,把所有人的该属性减\(1\),求一段最长的区间,满足执行所有操作之后该区间中所有人的所有属性全部为\(0\)


Solution

转换一下思考方向,求一段最长的区间,满足区间中每项属性的最大值之和不大于\(k\)

注意到单调性,固定右端点,移动左端点时,区间中所有属性的最大值之和一定是单调不增的。于是可以用双指针来枚举右端点寻找合法的最长区间。

剩下的就是如何快速得到区间中的某项属性的最大值,暴力枚举会使复杂度达到\(O(n^2m)\)级别,对于在双指针的应用场景维护区间最值,这时就应当想到使用单调队列进行优化。

时间复杂度\(O(nm)\)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;

int main() {
    int n,m,k;
    cin>>n>>m>>k;
    vector<vector<int>> a(n,vector<int>(m));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>a[i][j];
        }
    }
    vector<deque<int>> q(m);
    int ans=0;
    vector<int> res(m);
    for(int l=0,r=0;r<n;r++){
        int sum=0;
        for(int j=0;j<m;j++){
            while(!q[j].empty()&&a[q[j].back()][j]<a[r][j]){
                q[j].pop_back();
            }
            q[j].push_back(r);
            sum+=a[q[j].front()][j];
        }
        while(l<=r&&sum>k){
            l++;
            for(int i=0;i<m;i++){
                while(!q[i].empty()&&q[i].front()<l){
                    sum-=a[q[i].front()][i];
                    q[i].pop_front();
                    if(q[i].size()) sum+=a[q[i].front()][i];
                }
            }
        }
        if(r-l+1>ans){
            ans=r-l+1;
            for(int i=0;i<m;i++) res[i]=a[q[i].front()][i];
        }
    }
    for(int i=0;i<m;i++){
        cout<<res[i]<<" ";
    }
    return 0;
}
posted @ 2024-01-14 16:41  fengxue-K  阅读(26)  评论(0)    收藏  举报