CF2205D

[复制链接]
发表于 2026-5-30 13:18:39 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
择要:单调栈

前置芝士:洛谷单调栈模板题
原题指路:CF原题 洛谷翻译版
题意转化

要求删除次数最少,相称于求出一个最长的 cool。
而显然:求最长的 cool 相称于是求最长的单谷序列。
由于多谷则一定有峰,那么还需再删;
而单调序列相称于一端点是谷,故也可以用单谷序列去思量。
云云,原题题意就转化为求最长单谷序列的长度。
而答案最小删除次数则用原分列长度减去此值即可。
思绪

那么怎样求最长单谷序列的长度?
可以想到用单调栈完成。

  • 先从左往右扫一遍,维护一个单减栈。
    不难发现,当新参加的元素大于栈顶元素 \(a_x\) 时,栈内元素序列便是 \(a_x\) 左侧含 \(a_x\) 的最大单减序列,而栈的巨细便是其长度。
  • 再从右往左扫一遍,维护一个单减栈。
    不难发现,当新参加的元素大于栈顶元素 \(a_x\) 时,栈内元素序列便是 \(a_x\) 右侧含 \(a_x\) 的最大单增序列,而栈的巨细便是其长度。
对于一个元素 \(a_x\) 而言,把上述的两种序列拼起来,可见就得到了 \(a_x\) 为谷的最大单谷序列。
由于 \(a_x\) 被数了两次,以是 \(a_x\) 为谷的最长单谷序列长度 \(mxl_x =  dl_x + dr_x - 1\)。
留意别忘了每次末了还要处置惩罚留在栈中的元素。
那么就做完了。
举例

这么讲大概有点抽象。我以样例中的一组数据大概模拟一遍该过程。
样例中的一组数据是如许的:
4 1 3 2 5

  • 从左到右扫,维护单减栈。

    • 压入 \(4\),栈内 4。
    • 压入 \(1\),栈内 4 1。
    • 压入 \(3\)。
      \(3 > 1\),以是弹出 \(1\)。
      可见此时栈内的 4 1 就是 \(1\) 左侧含 \(1\) 的最大单减序列。求出相应的有关值。
      栈内 4 3。
    • 压入 \(2\),栈内 4 3 2。
    • 压入 \(5\),栈内全部元素均比它小,以是都弹出,并求出相干对应值。
      栈内 5。
    末了弹出栈中全部元素。与前面逻辑同理,此时的每一个元素的 \(dl_x\) 值仍为它即将弹出前的栈的巨细。

  • 从右往左扫,维护单减栈。
    逻辑也是一样的,这里就不赘述了。
代码

有解释版本[code]#includeusing namespace std;const int N = 5e5 + 10;int T;int n, a[N];int s[N], tops;int dl[N], dr[N];int ans;int main(){                scanf("%d", &T);        while(T--){                scanf("%d", &n);                for(int i = 1; i  0){                    dl[s[tops]] = tops;                    --tops;                }                                tops = 0;                for(int i = n; i >= 1; --i){                         if(tops < 1){                                s[++tops] = i;                                continue ;                        }                        while(a[s[tops]] < a && tops > 0){                                dr[s[tops]] = tops;                                --tops;                        }                        s[++tops] = i;                }                while(tops > 0){                    dr[s[tops]] = tops;                    --tops;                }                                                int mxl = -1;                for(int i = 1; i = 1; --i){ //从右到左扫,维护单减栈                         if(tops < 1){                                s[++tops] = i;                                continue ;                        }                        while(a[s[tops]] < a && tops > 0){ //记得判断tops                                 dr[s[tops]] = tops;                                --tops;                        }                        s[++tops] = i;                }                while(tops > 0){ //记得处置惩罚栈中剩余元素                     dr[s[tops]] = tops;                    --tops;                }                                                int mxl = -1;                for(int i = 1; i
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表