2024信友隊藍潤暑期集訓提高1班②Day3

TangyixiaoQAQ發表於2024-07-16

前言

noip毒瘤給我們講上午的知識

知識總結

題目

T1 【模板】單調棧

題目描述

題目描述:
給出項數為 n 的整數數列 a1…n,定義函式 f(i) 代表數列中第 i 個元素之後第一個大於 ai 的元素的下標,即 f(i)=min i<j<=n,aj>ai {j}。若不存在,則 f(i)=0。

試求出 f(1…n)。

輸入格式:
第一行一個正整數 n。

第二行 n 個正整數 a1…n​。

輸出格式;
一行 n 個整數 f(1…n) 的值。

樣例輸入:
5
1 4 2 3 5
樣例輸出:
2 5 4 5 0
資料規模:
$n≤3*106$,$a_i≤1*109$。

思路解析

單調棧的簡單運用。
考慮維護一個單調遞減的棧。

程式碼實現

#include <bits/stdc++.h>
using namespace std;
const int N = 3e6 + 5;
int n, a[N], f[N];
stack<int> s;
signed main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for (int i = n; i >= 1; i--) {
        while (!s.empty() && a[s.top()] <= a[i]) {
            s.pop();
        }
        if (!s.empty()) {
            f[i] = s.top();
        }
        s.push(i);
    }
    for (int i = 1; i <= n; i++) {
        printf("%d ", f[i]);
    }
    return 0;
}

T2 動物園的等待

題目描述

https://www.luogu.com.cn/problem/P1823

思路解析

只考慮每個人與其前面的人產生的數對。
維護一個自下而上遞減的棧。
在彈出的過程中,記錄答案即可。

程式碼實現

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct node {
    int v, id;
};
stack<node> s;
int n, h, ans;
signed main() {
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &h);
        node now = {h, 1};
        for (; !s.empty() && s.top().v <= h; s.pop()) {
            ans += s.top().id;
            if (s.top().v == h)
                now.id += s.top().id;
        }

        if (!s.empty()) {
            ans++;
        }
        s.push(now);
    }
    printf("%lld\n", ans);
    return 0;
}

T3 雨點

題目描述

https://www.luogu.com.cn/problem/P2698

思路解析

首先注意到答案一定是在端點上,否則出頭的部分就被浪費了。
按 x 排序後時間差等價於 y 的極差。
考慮二分答案後用單調佇列或者 ST 表維護極差。
不用二分答案也可以直接用雙指標+單調佇列去維護。

程式碼實現

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

typedef long long ll;
const int maxn = 100000;
const ll INF = 1e18;

ll n, D;
pair<ll, ll> a[maxn + 50];

#define x first
#define y second
int q1[maxn + 50], q2[maxn + 50];
int h1 = 1, t1 = 0;
int h2 = 1, t2 = 0;

int main() {
    ios_base::sync_with_stdio(false);
    cin >> n >> D;
    for (int i = 1; i <= n; i++)
        cin >> a[i].x >> a[i].y;
    sort(a + 1, a + n + 1);
    ll ans = INF;
    for (int i = 1, j = 1; i <= n; i++) {
        for (; h1 <= t1 && q1[h1] < i;)
            h1++;
        for (; h2 <= t2 && q2[h2] < i;)
            h2++;
        bool flag = false;
        for (; j <= n; j++) {
            if (h1 > t1 || h2 > t2) {
                q1[++t1] = j;
                q2[++t2] = j;
                continue;
            }
            if (a[q1[h1]].y - a[q2[h2]].y >= D) {
                flag = true;
                break;
            }
            for (; h1 <= t1 && a[q1[t1]].y <= a[j].y;)
                t1--;
            for (; h2 <= t2 && a[q2[t2]].y >= a[j].y;)
                t2--;
            q1[++t1] = j;
            q2[++t2] = j;
        }
        if (!flag)
            break;
        ans = min(ans, abs(a[i].x - a[j - 1].x));
    }
    if (ans == INF)
        cout << -1 << "\n";
    else
        cout << ans << "\n";
    return 0;
}

T4 jxcakak

題目描述

P2216 HAOI2007 理想的正方形 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)

思路解析

最大值和最小值是獨立的,可以單獨求解。
考慮用單調佇列去維護最大/小值,分行和列依次處理。

程式碼實現

#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5, inf = 0x3f3f3f3f;
int a[N][N], w, h, n, ans = inf;
struct node {
    int h, t, pos[N], val[N], flag;
    void init(int f) {
        memset(pos, 0, sizeof(pos));
        memset(val, 0, sizeof(val));
        h = 1;
        t = 0;
        flag = f;
        return;
    }
    void pop_front(int lim) {
        for (; h <= t && pos[h] <= lim;)
            h++;
    }
    void push_back(int p, int v) {
        for (; h <= t && (val[t] - v) * flag <= 0;)
            t--;
        t++;
        pos[t] = p;
        val[t] = v;
        return;
    }
    int get_first() {
        return val[h];
    }
} colmx[N], colmn[N], rowmx, rowmn;
signed main() {
    scanf("%d%d%d", &w, &h, &n);
    for (int i = 1; i <= w; i++) {
        for (int j = 1; j <= h; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    for (int i = 1; i <= w; i++) {
        colmx[i].init(1);
        colmn[i].init(-1);
    }
    for (int j = 1; j <= h; j++) {
        for (int i = 1; i <= w; i++) {
            colmx[i].pop_front(j - n);
            colmn[i].pop_front(j - n);
            colmx[i].push_back(j, a[i][j]);
            colmn[i].push_back(j, a[i][j]);
        }
        if (j >= n) {
            rowmx.init(1);
            rowmn.init(-1);
            for (int i = 1; i <= w; i++) {
                rowmx.pop_front(i - n);
                rowmn.pop_front(i - n);
                rowmx.push_back(i, colmx[i].get_first());
                rowmn.push_back(i, colmn[i].get_first());
                if (i >= n)
                    ans = min(ans, rowmx.get_first() + rowmn.get_first());
            }
        }
    }
    printf("%d", ans);
    return 0;
}

T5 澤澤在英國

題目描述

澤澤用了100000000000000000000 mod 10天的時間爬出了長城。長城的另一端是一條隧道,澤澤走了進去……

澤澤不小心又到了英國。英國多雨,基本上隔2天就要下一場雨。澤澤人品不好,到這裡的時候天正在下酸雨。

酸雨會腐蝕建築物,讓那些建築物顯得很難看。英國有家工廠免費為一條街道的建築物的牆面塗油漆。心腸雖好,但是由於技術問題,他們只能塗出一個矩形。現在由於酸雨事態嚴重,街道辦主任下命令塗出面積最大的矩形。

街道上的建築物高度參差不齊,那該怎麼辦呢?

他們想到了澤澤。

澤澤接到了這個任務,就去測量了這個街道上的所有建築物的高度。

請根據澤澤的資料,計算出最大面積。

思路解析

答案矩形的高度一定是某個建築的高度。
用單調棧求出左右兩側能夠到達的寬度即可。

程式碼實現

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, a[N], h, l, ans;
signed main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        h = max(h, a[i]);
    }
    for (int i = 1; i <= h; i++) {
        l = 0;
        for (int j = 1; j <= n; j++) {
            if (a[j] >= i) {
                l++;
            } else {
                l = 0;
            }
            ans = max(i * l, ans);
        }
    }
    printf("%d", ans);
    return 0;
}

相關文章