从这几篇博客学习的:
 DP优化小技巧(单调队列/单调栈)
 (单调队列优化DP) 代码源每日一题 Div1 选元素(数据加强版)
 算法学习笔记(67): 单调栈
 牛客多校第九场I (单调栈优化dp/单调栈的常用套路)
一. 单调队列
NC50528 滑动窗口

 
主要思想:
 假设我们需要维护长度为
    
     
      
       
        k
       
      
      
       k
      
     
    k的区间最大值,遍历过程中,对于一个数字
    
     
      
       
        
         a
        
        
         
          i
         
         
          +
         
         
          1
         
        
       
      
      
       a_{i+1}
      
     
    ai+1,如果
    
     
      
       
        
         a
        
        
         
          i
         
         
          +
         
         
          1
         
        
       
       
        >
       
       
        =
       
       
        
         a
        
        
         i
        
       
      
      
       a_{i+1} >= a_i
      
     
    ai+1>=ai,那么我们完全可以把
    
     
      
       
        
         a
        
        
         i
        
       
      
      
       a_i
      
     
    ai的影响忽略掉。因为后面的数字比你并且生命周期还比你大,所以最大值永远不可能取到
    
     
      
       
        
         a
        
        
         i
        
       
      
      
       a_i
      
     
    ai。具体在队列中的做法就是不断访问队尾元素,小于等于当前值就出队,结束后将当前元素入队。这样的话就可以保证队首元素一定是长度为
    
     
      
       
        k
       
      
      
       k
      
     
    k的区间的最大值。(当然你需要判断队首元素与当前位置距离与
    
     
      
       
        k
       
      
      
       k
      
     
    k的大小关系确定队首元素是否需要出队)。这个过程可以用deque很容易实现,数组模拟也很容易。
 AC代码:
 deque:
#include <bits/stdc++.h>
using namespace std;
int n, k;
int ar[1000050];
deque<int> q;
int main()
{
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; ++i) scanf("%d", &ar[i]);
    for(int i = 1; i <= n; ++i)
    {
        while(!q.empty() && q.front() + k <= i) q.pop_front();
        while(!q.empty() && ar[q.back()] >= ar[i]) q.pop_back();
        q.push_back(i);
        if(i >= k) printf("%d ", ar[q.front()]);
    }
    putchar('\n');
    q.clear();
    for(int i = 1; i <= n; ++i)
    {
        while(!q.empty() && q.front() + k <= i) q.pop_front();
        while(!q.empty() && ar[q.back()] <= ar[i]) q.pop_back();
        q.push_back(i);
        if(i >= k) printf("%d ", ar[q.front()]);
    }
    return 0;
}
数组模拟:
#include <bits/stdc++.h>
using namespace std;
int n, k;
int ar[1000050];
int p[1000050];
int l, r;//左闭右开
int main()
{
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; ++i) scanf("%d", &ar[i]);
    l = 0;
    r = 1;
    p[0] = 1;
    if(k == 1) printf("%d ", ar[1]);
    for(int i = 2; i <= n; ++i)
    {
        if(i - p[l] >= k && (l < r))    l++;
        while(r > l && ar[p[r - 1]] >= ar[i]) r--;
        p[r++] = i;
        if(i >= k)  printf("%d ", ar[p[l]]);
    }
    putchar('\n');
    l = 0;
    r = 1;
    p[0] = 1;
    if(k == 1)  printf("%d ", ar[1]);
    for(int i = 2; i <= n; ++i)
    {
        if(i - p[l] >= k && (l < r))    l++;
        while(r > l && ar[p[r - 1]] <= ar[i])    r--;
        p[r++] = i;
        if(i >= k)  printf("%d ", ar[p[l]]);
    }
    putchar('\n');
    return 0;
}
二. 单调队列优化dp
当我们为了实现给动态规划的复杂度降维的时候,通常就需要单调栈/队列,通常用来维护前面状态下可以取到的最大值或者最小值,然后直接进行转移。(ygg)
 
1.daimayuan #875. 选元素(数据加强版)
首先,我们可以看数据没加强版
AcWing 4418. 选元素

 
 题意:
 就是对与每个长度为为
    
     
      
       
        k
       
      
      
       k
      
     
    k的子区间,至少要有一个数被选择,一共可以选择
    
     
      
       
        x
       
      
      
       x
      
     
    x个数字,目标是让选择的
    
     
      
       
        x
       
      
      
       x
      
     
    x个数字最大。
 分析:
 考虑dp,定义dp[i][j]表示选了ar[i]的情况下,前
    
     
      
       
        i
       
      
      
       i
      
     
    i个数字一共有
    
     
      
       
        j
       
      
      
       j
      
     
    j个被选的情况下的最大值。
 状态转移方程:
    
     
      
       
        d
       
       
        p
       
       
        [
       
       
        i
       
       
        ]
       
       
        [
       
       
        j
       
       
        ]
       
       
        =
       
       
        m
       
       
        a
       
       
        x
       
       
        (
       
       
        d
       
       
        p
       
       
        [
       
       
        i
       
       
        ]
       
       
        [
       
       
        j
       
       
        ]
       
       
        ,
       
       
        d
       
       
        p
       
       
        [
       
       
        i
       
       
        −
       
       
        k
       
       
        ,
       
       
        i
       
       
        −
       
       
        1
       
       
        ]
       
       
        [
       
       
        j
       
       
        −
       
       
        1
       
       
        ]
       
       
        +
       
       
        a
       
       
        r
       
       
        [
       
       
        i
       
       
        ]
       
       
        )
       
      
      
       dp[i][j] =max(dp[i][j],dp[i-k,i-1][j-1]+ar[i])
      
     
    dp[i][j]=max(dp[i][j],dp[i−k,i−1][j−1]+ar[i])。复杂度
    
     
      
       
        O
       
       
        (
       
       
        
         n
        
        
         3
        
       
       
        )
       
      
      
       O(n^3)
      
     
    O(n3)。
 AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
int n, k, x;
int ar[205];
int dp[205][205];
int ans;
signed main()
{
    scanf("%lld%lld%lld", &n, &k, &x);
    for(int i = 1; i <= n; ++i) scanf("%lld", &ar[i]);
    memset(dp, 128, sizeof(dp));
    //cout << dp[0][0] << '\n';
    dp[0][0] = 0;
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= x; ++j)
        {
            for(int p = max(0ll, i - k); p < i; ++p)
            {
                dp[i][j] = max(dp[i][j], dp[p][j - 1] + ar[i]);
            }
            //cout << i << ' ' << j << ' ' << dp[i][j] << '\n';
        }
    }
    ans = -1;
    for(int i = n - k + 1; i <= n; ++i) ans = max(ans, dp[i][x]);
    printf("%lld\n", ans);
    return 0;
}
数据加强版
对于数据加强版,
    
     
      
       
        n
       
       
        ,
       
       
        k
       
       
        ,
       
       
        x
       
      
      
       n,k,x
      
     
    n,k,x的取值达到2500,
    
     
      
       
        O
       
       
        (
       
       
        
         n
        
        
         3
        
       
       
        )
       
      
      
       O(n^3)
      
     
    O(n3)一定超时。我们考虑优化,对于原来的状态转移式。
    
     
      
       
        d
       
       
        p
       
       
        [
       
       
        i
       
       
        ]
       
       
        [
       
       
        j
       
       
        ]
       
       
        =
       
       
        m
       
       
        a
       
       
        x
       
       
        (
       
       
        d
       
       
        p
       
       
        [
       
       
        i
       
       
        ]
       
       
        [
       
       
        j
       
       
        ]
       
       
        ,
       
       
        d
       
       
        p
       
       
        [
       
       
        i
       
       
        −
       
       
        k
       
       
        ,
       
       
        i
       
       
        −
       
       
        1
       
       
        ]
       
       
        [
       
       
        j
       
       
        −
       
       
        1
       
       
        ]
       
       
        +
       
       
        a
       
       
        r
       
       
        [
       
       
        i
       
       
        ]
       
       
        )
       
      
      
       dp[i][j] =max(dp[i][j],dp[i-k,i-1][j-1]+ar[i])
      
     
    dp[i][j]=max(dp[i][j],dp[i−k,i−1][j−1]+ar[i]),考虑以极快的速度求出
    
     
      
       
        m
       
       
        a
       
       
        x
       
       
        (
       
       
        d
       
       
        p
       
       
        [
       
       
        i
       
       
        −
       
       
        k
       
       
        ,
       
       
        i
       
       
        −
       
       
        1
       
       
        ]
       
       
        [
       
       
        j
       
       
        −
       
       
        1
       
       
        ]
       
       
        )
       
      
      
       max(dp[i-k,i-1][j-1])
      
     
    max(dp[i−k,i−1][j−1])。本质上一个窗口大小为
    
     
      
       
        k
       
      
      
       k
      
     
    k的滑动窗口,故利用单调队列优化即可。复杂度
    
     
      
       
        O
       
       
        (
       
       
        
         n
        
        
         2
        
       
       
        )
       
      
      
       O(n^2)
      
     
    O(n2)。
 AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, k, x, top;
ll ar[2550];
ll dp[2550][2550];
ll ans;
int main()
{
    scanf("%d%d%d", &n, &k, &x);
    for(int i = 1; i <= n; ++i) scanf("%lld", &ar[i]);
    memset(dp, 128, sizeof(dp));
    //cout << dp[0][0] << '\n';
    dp[0][0] = 0;
    for(int j = 1; j <= x; ++j)
    {
        deque<int> q;
        q.push_back(0);
        for(int i = 1; i <= n; ++i)
        {
            while(!q.empty() && q.front() < i - k) q.pop_front();
            dp[i][j] = dp[q.front()][j - 1] + ar[i];
            while(!q.empty() && dp[q.back()][j - 1] <= dp[i][j - 1]) q.pop_back();
            q.push_back(i);
        }
    }
    ans = -1;
    for(int i = n - k + 1; i <= n; ++i) ans = max(ans, dp[i][x]);
    printf("%lld\n", ans);
    return 0;
}
2. C. Jump and Treasure(Gym - 103743C)2022江苏省赛

 
 题意:
 输入
    
     
      
       
        n
       
       
        ,
       
       
        q
       
       
        ,
       
       
        p
       
      
      
       n,q,p
      
     
    n,q,p,之后一行
    
     
      
       
        n
       
      
      
       n
      
     
    n个数字,之后
    
     
      
       
        q
       
      
      
       q
      
     
    q行,
    
     
      
       
        q
       
      
      
       q
      
     
    q次询问。
 你现在在玩一个游戏,初始在0点,你只可以向右走,游戏有很多关,对于第
    
     
      
       
        x
       
      
      
       x
      
     
    x关,你只能走到
    
     
      
       
        i
       
      
      
       i
      
     
    i的倍数的点上,并且每步跨越的最大距离是
    
     
      
       
        p
       
      
      
       p
      
     
    p,每个点上有数字,当你到达这个点是,你的数值就会加上该点的权值,初始时你的数值为0,通关条件是到达点
    
     
      
       
        n
       
       
        +
       
       
        1
       
      
      
       n+1
      
     
    n+1及其之后的点,并且
    
     
      
       
        n
       
       
        +
       
       
        1
       
      
      
       n+1
      
     
    n+1及其之后的点的数值都为0,走到
    
     
      
       
        n
       
       
        +
       
       
        1
       
      
      
       n+1
      
     
    n+1之后的点不需要考虑他们是否是
    
     
      
       
        x
       
      
      
       x
      
     
    x的倍数,只需要考虑最大跨越距离的限制。你可以在不违规的前提下走任意多步,
    
     
      
       
        q
       
      
      
       q
      
     
    q次询问,问你对于第
    
     
      
       
        x
       
      
      
       x
      
     
    x关,通关时的最大数值。
 分析:
 考虑dp,定义dp[i]表示,对于第
    
     
      
       
        x
       
      
      
       x
      
     
    x关,走到第
    
     
      
       
        i
       
      
      
       i
      
     
    i个能到达的点时所能获得的最大价值。
 转移方程:
    
     
      
       
        d
       
       
        p
       
       
        [
       
       
        i
       
       
        ]
       
       
        =
       
       
        m
       
       
        a
       
       
        x
       
       
        (
       
       
        d
       
       
        p
       
       
        [
       
       
        i
       
       
        ]
       
       
        ,
       
       
        d
       
       
        p
       
       
        [
       
       
        i
       
       
        −
       
       
        p
       
       
        /
       
       
        x
       
       
        ,
       
       
        i
       
       
        −
       
       
        1
       
       
        ]
       
       
        +
       
       
        a
       
       
        r
       
       
        [
       
       
        i
       
       
        ]
       
       
        )
       
      
      
       dp[i] = max(dp[i], dp[i-p/x,i-1]+ar[i])
      
     
    dp[i]=max(dp[i],dp[i−p/x,i−1]+ar[i]) (不考虑Noob的情况)。
 如果暴力跑,复杂度为
    
     
      
       
        O
       
       
        (
       
       
        T
       
       
        L
       
       
        E
       
       
        )
       
      
      
       O(TLE)
      
     
    O(TLE),依旧考虑单调队列优化取
    
     
      
       
        m
       
       
        a
       
       
        x
       
      
      
       max
      
     
    max的部分。复杂度
    
     
      
       
        O
       
       
        (
       
       
        n
       
       
        l
       
       
        o
       
       
        g
       
       
        n
       
       
        )
       
      
      
       O(nlogn)
      
     
    O(nlogn)。
 AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
const  ll inf = 0x3f3f3f3f3f3f3f3f;
int n, p, q, x;
ll ans[1000050];
int ar[1000050];
ll dp[1000050];
vector<int> vt[1000005];
int qq[1000050];
int l, r;
int main()
{
    io;
    cin >> n >> q >> p;
    for(int i = 1; i <= n; ++i) cin >> ar[i];
    for(int i = 1; i <= n; ++i)
    {
        ans[i] = -inf;
        for(int j = 0; j <= n; j += i) vt[i].push_back(j);
        vt[i].push_back(n + 1);
    }
    while(q--)
    {
        cin >> x;
        if(ans[x] != -inf) cout << ans[x] << '\n';
        else if(x > p) cout << "Noob\n";
        else
        {
            dp[0] = 0;
            l = r = 1;
            qq[r] = 0;
            for(int i = 1; i < vt[x].size(); ++i)
            {
                while(l <= r && vt[x][i] - qq[l] > p) ++l;
                dp[vt[x][i]] = dp[qq[l]] + ar[vt[x][i]];
                while(l <= r && dp[qq[r]] <= dp[vt[x][i]]) --r;
                qq[++r] = vt[x][i];
            }
            ans[x] = dp[n + 1];
            cout << ans[x] << '\n';
        }
    }
    return 0;
}
3.P1725 琪露诺(单调队列)

 题意:
 很类似上一个题,只不过跳的方式有所不同,是给定一个
    
     
      
       
        l
       
       
        ,
       
       
        r
       
      
      
       l,r
      
     
    l,r,对于当前点
    
     
      
       
        i
       
      
      
       i
      
     
    i,可以跳到
    
     
      
       
        [
       
       
        i
       
       
        +
       
       
        l
       
       
        ,
       
       
        i
       
       
        +
       
       
        r
       
       
        ]
       
      
      
       [i+l,i+r]
      
     
    [i+l,i+r]
 AC代码:
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int n, l, r;
int ar[600050];
int dp[600050];
deque<int> q;
int main()
{
    scanf("%d%d%d", &n, &l, &r);
    for(int i = 0; i <= n; ++i)
    {
        scanf("%d", &ar[i]);
        dp[i] = -inf;
    }
    dp[0] = ar[0];
    q.push_back(0);
    dp[l] = ar[l];
	//3*n是我乱写的,反正不会超时,后面那一坨的dp值都是相同的。
    for(int i = l + 1; i <= 3 * n; ++i)
    {
        while(!q.empty() && q.front() + r < i) q.pop_front();
        while(!q.empty() && dp[q.back()] <= dp[i - l]) q.pop_back();
        q.push_back(i - l);
        dp[i] = dp[q.front()] + ar[i];
        //cout << i << ' ' << dp[i] << '\n';
    }
    printf("%d\n", dp[3 * n]);
    return 0;
}
三. 单调栈
两大用处:
 1.NGE问题(Next Greater Element),也就是,对序列中每个元素,找到下一个比它大的元素。
 2.两元素间所有元素均(不)大/小于这两者
1. P5788 【模板】单调栈

 
 (其实也可以用单调队列,不需要队首元素出队的单调队列)
 AC代码:
#include <bits/stdc++.h>
using namespace std;
int n;
int ar[3000050];
stack<int> st;
int ans[3000050];
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &ar[i]);
    ans[n] = 0;
    st.push(n);
    for(int i = n - 1; i > 0; --i)
    {
        while(!st.empty() && ar[st.top()] <= ar[i]) st.pop();
        if(!st.empty()) ans[i] = st.top();
        else ans[i] = 0;
        st.push(i);
    }
    for(int i = 1; i <= n; ++i) printf("%d ", ans[i]);
    putchar('\n');
    return 0;
}
2.P1823 [COI2007] Patrik 音乐会的等待

 
 (区间1~i内元素大小关系和单调栈内元素情况)
 
 
 AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
int n;
int ar[500050];
stack<pair<int, int> > st;
pair<int, int> pi;
int ans, cnt;
signed main()
{
    scanf("%lld", &n);
    for(int i = 1; i <= n; ++i) scanf("%lld", &ar[i]);
    st.push({ar[1], 1});
    for(int i = 2; i <= n; ++i)
    {
        int cnt = 1;
        while(!st.empty() && st.top().first <= ar[i])
        {
            pi = st.top();
            st.pop();
            //cout << pi.first << ' ' << pi.second << '\n';
            ans += pi.second;
            if(pi.first == ar[i]) cnt = pi.second + 1;
            else cnt = 1;
        }
        if(!st.empty()) ++ans;
        st.push({ar[i], cnt});
        //cout << i << ' ' << ans << '\n';
    }
    printf("%lld\n", ans);
    return 0;
}
四. 单调栈优化dp
1.CF1313C2 Skyscrapers (hard version)

 
 题意:
 输入一个
    
     
      
       
        n
       
      
      
       n
      
     
    n,之后输入
    
     
      
       
        n
       
      
      
       n
      
     
    n个数字
    
     
      
       
        a
       
       
        r
       
       
        [
       
       
        i
       
       
        ]
       
      
      
       ar[i]
      
     
    ar[i]。
 让你选择一个点作为山峰。假设选择点
    
     
      
       
        p
       
       
        o
       
       
        s
       
      
      
       pos
      
     
    pos作为山峰,其他的
    
     
      
       
        n
       
       
        −
       
       
        1
       
      
      
       n-1
      
     
    n−1点的高度将发生变化。假设修改后的高度记为
    
     
      
       
        h
       
       
        [
       
       
        i
       
       
        ]
       
      
      
       h[i]
      
     
    h[i],对于1~pos-1中的一个点i,必须满足
    
     
      
       
        h
       
       
        [
       
       
        i
       
       
        ]
       
       
        =
       
       
        m
       
       
        i
       
       
        n
       
       
        (
       
       
        h
       
       
        [
       
       
        i
       
       
        +
       
       
        1
       
       
        ]
       
       
        ,
       
       
        a
       
       
        r
       
       
        [
       
       
        i
       
       
        ]
       
       
        )
       
      
      
       h[i]=min(h[i+1],ar[i])
      
     
    h[i]=min(h[i+1],ar[i]),对于pos+1~n中的一个点i,必须满足
    
     
      
       
        h
       
       
        [
       
       
        i
       
       
        ]
       
       
        =
       
       
        m
       
       
        i
       
       
        n
       
       
        (
       
       
        h
       
       
        [
       
       
        i
       
       
        −
       
       
        1
       
       
        ]
       
       
        ,
       
       
        a
       
       
        r
       
       
        [
       
       
        i
       
       
        ]
       
       
        )
       
      
      
       h[i] = min(h[i-1],ar[i])
      
     
    h[i]=min(h[i−1],ar[i])。求
    
     
      
       
        
         ∑
        
        
         1
        
        
         n
        
       
       
        h
       
       
        [
       
       
        i
       
       
        ]
       
      
      
       \sum_1^n h[i]
      
     
    ∑1nh[i]的最大值。输出取最大值时的h[i]数组。
 分析:
 首先,我们可以看一下easy版本。区别在于n的大小。easy版本n最大1000。
    
     
      
       
        O
       
       
        (
       
       
        
         n
        
        
         2
        
       
       
        )
       
      
      
       O(n^2)
      
     
    O(n2)能过。我们考虑dp,定义dp1[i],dp2[i]分别表示选择
    
     
      
       
        i
       
      
      
       i
      
     
    i作为山峰时
    
     
      
       
        i
       
      
      
       i
      
     
    i极其左侧的最大值。答案就是
    
     
      
       
        m
       
       
        a
       
       
        x
       
       
        (
       
       
        d
       
       
        p
       
       
        1
       
       
        [
       
       
        i
       
       
        ]
       
       
        +
       
       
        d
       
       
        p
       
       
        2
       
       
        [
       
       
        i
       
       
        ]
       
       
        −
       
       
        a
       
       
        r
       
       
        [
       
       
        i
       
       
        ]
       
       
        )
       
      
      
       max(dp1[i]+dp2[i]-ar[i])
      
     
    max(dp1[i]+dp2[i]−ar[i])。
 我们考虑快速求dp1[i]的方法,假设ar[j]是
    
     
      
       
        i
       
      
      
       i
      
     
    i之前第一个满足ar[j]<=ar[i]的元素,那么
    
     
      
       
        d
       
       
        p
       
       
        1
       
       
        [
       
       
        i
       
       
        ]
       
       
        =
       
       
        d
       
       
        p
       
       
        1
       
       
        [
       
       
        j
       
       
        ]
       
       
        +
       
       
        (
       
       
        i
       
       
        −
       
       
        j
       
       
        )
       
       
        ∗
       
       
        a
       
       
        r
       
       
        [
       
       
        i
       
       
        ]
       
      
      
       dp1[i]=dp1[j]+(i-j)*ar[i]
      
     
    dp1[i]=dp1[j]+(i−j)∗ar[i]。而对于第一个小于等于的元素,显然可以利用单调栈快速找出。(dp2求法同理)复杂度
    
     
      
       
        O
       
       
        (
       
       
        n
       
       
        )
       
      
      
       O(n)
      
     
    O(n)。
 (单调栈用法1,利用单调栈找到区间内第一个比他大/小的元素)
 AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, pos;
ll ar[500050];
ll dp1[500050];
ll dp2[500050];
ll ans[500050];
ll mx;
stack<ll> st;
void work()
{
    int tmp = pos;
    ll mxx = ar[tmp];
    ans[tmp] = ar[tmp];
    while(tmp > 0)
    {
        --tmp;
        ans[tmp] = min(mxx, ar[tmp]);
        mxx = ans[tmp];
    }
    tmp = pos;
    mxx = ar[tmp];
    while(tmp < n)
    {
        ++tmp;
        ans[tmp] = min(mxx, ar[tmp]);
        mxx = ans[tmp];
    }
    for(int i = 1; i <= n; ++i) printf("%lld ", ans[i]);
    putchar('\n');
}
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%lld", &ar[i]);
    st.push(0);
    for(int i = 1; i <= n; ++i)
    {
        while(!st.empty() && ar[st.top()] >= ar[i]) st.pop();
        dp1[i] = dp1[st.top()] + (i - st.top()) * ar[i];
        st.push(i);
    }
    while(!st.empty()) st.pop();
    st.push(n + 1);
    for(int i = n; i > 0; --i)
    {
        while(!st.empty() && ar[st.top()] >= ar[i]) st.pop();
        dp2[i] = dp2[st.top()] + (st.top() - i) * ar[i];
        st.push(i);
    }
    for(int i = 1; i <= n; ++i)
    {
        if(dp1[i] + dp2[i] - ar[i] > mx)
        {
            mx = dp1[i] + dp2[i] - ar[i];
            pos = i;
        }
    }
    work();
    return 0;
}

 (295是正解,换成解绑之后的cin,cout用线段树暴力维护居然卡过了)。
2.CF1407D Discrete Centrifugal Jumps

 
 题意:
 输入一个n,之后n个数字ar[i]。
 一开始你在位置1,你要到达位置n,从位置i到位置j必须满足一下条件之一。
 
 问你到达n最少需要跳几步。
 分析:
 单调栈的用法2,找两元素间所有元素均(不)大/小于这两者
 
 (区间1~i内元素大小关系和单调栈内元素情况)
 借助这个图理解和P1823 [COI2007] Patrik 音乐会的等待这个题理解一下。
 AC代码:
#include <bits/stdc++.h>
using namespace std;
int n;
stack<int> st1, st2;
int dp[300050];
int ar[300050];
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &ar[i]);
    memset(dp, 0x3f, sizeof(dp));
    dp[1] = 0;
    for(int i = 1; i <= n; ++i)
    {
        dp[i] = min(dp[i], dp[i - 1] + 1);
        while(!st1.empty() && ar[st1.top()] <= ar[i])
        {
            int pos = st1.top();
            st1.pop();
            if(!st1.empty() && ar[i] > ar[pos]) dp[i] = min(dp[st1.top()] + 1, dp[i]);
        }
        st1.push(i);
        while(!st2.empty() && ar[st2.top()] >= ar[i])
        {
            int pos = st2.top();
            st2.pop();
            if(!st2.empty() && ar[i] < ar[pos]) dp[i] = min(dp[st2.top()] + 1, dp[i]);
        }
        st2.push(i);
    }
    printf("%d\n", dp[n]);
    return 0;
}
3.牛客多校第九场I The Great Wall II

 
 
 题意:
 输入一个n,之后输入数组ar[n]。你需要将区间[1,n]分成k个子区间,这k个区间两两不交,且并集是全集。每个子区间的值是这个区间中最大的那个数字,你需要最小化这k个区间的值的和。输出k取1~n时对应的答案。
 分析:
 官方题解:
 
 
 
 说人话:
 
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int n;
int ar[8005];
int dp[8005][8005];
struct node
{
    int val, mi, mi_dp;//ar[k],dp[k][j - 1],min(dp[k][j])
}st[8005];
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &ar[i]);
    memset(dp, 0x3f, sizeof(dp));
    dp[0][0] = 0;
    for(int j = 1; j <= n; ++j)
    {
        int top = 1;
        st[top] = {inf, inf, inf};
        for(int i = 1; i <= n; ++i)
        {
            int mi = dp[i - 1][j - 1];
            while(top >= 1 && st[top].val <= ar[i]) mi = min(mi, st[top--].mi);
            st[top + 1] = {ar[i], mi, min(mi + ar[i], st[top].mi_dp)};
            ++top;
            dp[i][j] = st[top].mi_dp;
        }
        printf("%d\n", dp[n][j]);
    }
    return 0;
}



















