跳转至

单调栈题单

练习 #1 [COCI2010-2011#3] DIFERENCIJA

题目描述

给出一个长度为 \(n\) 的序列 \(a_i\),求出下列式子的值:

\(\sum_{i=1}^{n} \sum_{j=i}^{n} (\max_{i\le k\le j} a_k-\min_{i\le k\le j} a_k)\)

即定义一个子序列的权值为序列内最大值与最小值的差。求出所有连续子序列的权值和。


考虑维护f,g分别表示\(1\sim i\)中所有后缀的min之和,max之和。那么每次以i为后缀的答案就是g-f。

g,f可以使用单调栈维护后缀min,max顺便维护。

// Problem: P6503 [COCI2010-2011#3] DIFERENCIJA
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6503
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)


#include<bits/stdc++.h>

using namespace std;
#define rd read()
#define ull unsigned long long
#define int long long 
#define itn int

int read(){
    int x;
    cin>>x;
    return x;
}
const int N=3e5+5;
const ull P=137;

/*

策略

我们考虑当前维护了一段前缀的后缀max和min

*/

int a[N];
int mx[N],topmx;
int mn[N],topmn;
signed main(){
    int n=rd;
    for(int i=1;i<=n;i++){
        a[i]=rd;
    }

    int ans=0;
    int f=0,g=0;//f:1~i的所有后缀的min之和

    for(int i=1;i<=n;i++){
        while(topmx&&a[mx[topmx]]<=a[i]){
            g-=a[mx[topmx]]*(mx[topmx]-mx[topmx-1]);
            topmx--;
        }
        while(topmn&&a[mn[topmn]]>a[i]){
            f-=a[mn[topmn]]*(mn[topmn]-mn[topmn-1]);
            topmn--;
        }

        f+=a[i]*(i-mn[topmn]);
        g+=a[i]*(i-mx[topmx]);
        mn[++topmn]=i;
        mx[++topmx]=i;
        ans+=g-f;
    }
    cout<<ans<<endl;
}

练习 #2 有重数单调栈 [COI2007] Patrik 音乐会的等待

题目描述

\(n\) 个人正在排队进入一个音乐会。人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人。

队列中任意两个人 \(a\)\(b\),如果他们是相邻或他们之间没有人比 \(a\)\(b\) 高,那么他们是可以互相看得见的。

写一个程序计算出有多少对人可以互相看见。

输入格式

输入的第一行包含一个整数 \(n\),表示队伍中共有 \(n\) 个人。

接下来的 \(n\) 行中,每行包含一个整数,表示人的高度,以毫微米(等于 \(10^{-9}\) 米)为单位,这些高度分别表示队伍中人的身高。

输出格式

输出仅有一行,包含一个数 \(s\),表示队伍中共有 \(s\) 对人可以互相看见。


注意下重复数字的累加,以及边界不要多加了。

// Problem: P1823 [COI2007] Patrik 音乐会的等待
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1823
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Challenger: Erica N
// ----
// 
#include<bits/stdc++.h>

using namespace std;
#define rd read()
#define ull unsigned long long
#define int long long 
#define pb push_back
#define itn int
#define ps second 
#define pf first


#define rd read()
int read()
{
  int xx = 0, ff = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9')
  {
    if (ch == '-')
      ff = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9')
    xx = xx * 10 + (ch - '0'), ch = getchar();
  return xx * ff;
}
#define zerol = 1
#ifdef zerol
#define cdbg(x...) do { cerr << #x << " -> "; err(x); } while (0)
void err() {
    cerr << endl;
}
template<template<typename...> class T, typename t, typename... A>
void err(T<t> a, A... x) {
    for (auto v: a) cerr << v << ' ';
    err(x...);
}
template<typename T, typename... A>
void err(T a, A... x) {
    cerr << a << ' ';
    err(x...);
}
#else
#define dbg(...)
#endif
const int N=5e5+5;
const ull P=137;
const int INF=1e18+7;
/*

策略


*/  


int a[N],top;
pair<int,int> stk[N];

signed main(){
    int n=rd;
    for(int i=1;i<=n;i++){
        a[i]=rd;
    }


    int ans=0;
    for(int i=1;i<=n;i++){
        //答案记载后面的人头上
        int cnt=0;
        while(top&&a[stk[top].pf]<=a[i]){
            if(a[stk[top].pf]==a[i])cnt+=stk[top].ps;
            ans+=stk[top].ps;
            top--;
        }
        if(top)ans++;// not +stk[top].ps
        stk[++top]={i,cnt+1};
        // cdbg(i,ans,cnt+1);
    }

    cout<<ans<<endl;
}

// 4 2