P3522 [POI2011] TEM-Temperature

原题链接

题解

尽量直观地理解单调队列的作用

首先,对于合法的一段,有如下性质 A 满足:

  • 当前的最高温度大于等于前面的最大的最低温度

该性质对于段内每一个数都满足,所以对于第 \(i\) 天,我们可以找其前面的第一天 \(j\) 的最低温度大于 \(i\) 的最高温度,同时还要满足 \((j,i]\) 内性质 A 满足

我们做如下分析:

1.如果 \(i\) 天的最高温度小于 \(i-1\) 天的最低温度,那么后面以任意天作为右端点的段的左端点都不可能小于 \(i\),即掉了

2.假设我们已知以 \(i-1\) 为右端点的段,且 \(r[i]\geq l[i-1]\) ,那么以 \(i\) 为右端点的段的左端点 \(left[i]\) 不可能小于 以 \(i-1\) 为右端点的段的左端点 \(left[i-1]\),因为需要满足 \([left[i-1],i-1]\) 内的点满足性质A

  1. 经过上述分析,我们发现,以每个点为右端点的段,其左端点是单调不降的,因此有点类似于 双指针,我们可以线性维护

  2. 在左端点右移的过程中,由于我们需要一直右移到 \([left[i],i]\) 内的最大的 \(l\) 不大于 \(r_i\) ,所以我们还需要添加一个数据结构来维护所有的 \(l\)

  3. 假设我们用数组,顺序维护 \(left[i],i\) 内所有的l,先不计时间复杂度,我们找到该范围内最大的 \(l\) ,判断其与 \(r_i\) 的大小,如果大了,把该 \(l\) 所在位置及其左边所有的 点,全部清空,如此一直循环,直至 \(l\leq r[i]\) 或者数组大小为零,便退出。

因此,我们可以从左到右维护一个 \(l\) 单调减,\(id\) 单调增的队列,如果队首元素大于 \(r_i\) 就弹出,代表其及其前面所有点都被清空,同时再添加一个数组累积队内元素到前面第一个大于该元素的数之间有多少不大于它的数,即其(队首)左边所有点的个数

code

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

int l[1000005],r[1000005];
int hide[1000005]={0};

void solve()
{
    int n;
    cin>>n;

    for(int i=1;i<=n;i++) cin>>l[i]>>r[i];

    deque<int> q;


    int ans=1;
    for(int i=1;i<=n;i++)
    {

        while(q.size()&&l[q.front()]>r[i]) q.pop_front();


        if(q.size()) ans=max(ans,i-q.front()+1+hide[q.front()]);

        while(q.size()&&l[q.back()]<=l[i])
        {
            hide[i]+=hide[q.back()]+1;
            q.pop_back();
        }
        q.push_back(i);
    }
    cout<<ans;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t=1;
    while(t--) solve();
    return 0;
}


posted @ 2024-07-21 16:59  纯粹的  阅读(41)  评论(0)    收藏  举报