1. 程式人生 > 其它 >CF689D Friends and Subsequences

CF689D Friends and Subsequences

題目大意

給定兩個大小為 \(n\) 的序列 \(a_i,b_i\),求有多少個數對 \((l,r)\),滿足 \(1\le l\le r\le n\)

\[\max_{i=l}^r a_i=\min_{i=l}^r b_i \]

\(1 \le n\le 2\times 10^5\)\(|a_i|,|b_i|\le 10^9\)

解題思路

ST + 二分。

首先考慮一個 \(\mathcal{O}(n^2)\) 演算法,不難想到,可以 \(\mathcal O(n \log n)\) 預處理 ST 表,然後暴力列舉 \((l,r)\) 看是否滿足要求。

我們把問題轉化一下,固定左界 \(l\)

,往右不難發現不考慮極端情況越靠近 \(l\)\(\max\{a_i\}\) 越會小於 \(\min\{b_i\}\),越靠近 \(n\)\(\max\{a_i\}\) 越會大於 \(\min\{b_i\}\),在 \([l,n]\) 中會存在一個區間 \([l',r']\) 使得這個區間裡的數滿足要求。

不難發現這個東西是單調的滿足單調,所以直接二分 \(l'\)\(r'\) 並判斷是否滿足要求,預處理可以用 ST 表完成,然後讓答案加上 \(r'-l'+1\) 即可。

時間複雜度 \(\mathcal O(n \log n+n \log n)\)

CODE

#include <bits/stdc++.h>
#define int long long

using namespace std;

int a[200086];
int b[200086];

int mmax[200086][20];
int mmin[200086][20];

int Max(int l, int r)
{
    if (l > r)
        swap(l, r);
    int k = log2(r - l + 1);
    return max(mmax[l][k], mmax[r - (1 << k) + 1][k]);
}

int Min(int l, int r)
{
    if (l > r)
        swap(l, r);
    int k = log2(r - l + 1);
    return min(mmin[l][k], mmin[r - (1 << k) + 1][k]);
}

int findl(int l, int r, int n)
{
    int left = l;
    while (l <= r)
    {
        int mid = (l + r) / 2;
        if (Max(left, mid) < Min(left, mid))
            l = mid + 1;
        else
            r = mid - 1;
    }
    if (l <= n && Max(left, l) == Min(left, l))
        return l;
    else
        return 0;
}

int findr(int l, int r, int n)
{
    int left = l;
    while (l <= r)
    {
        int mid = (l + r) / 2;
        if (Max(left, mid) > Min(left, mid))
            r = mid - 1;
        else
            l = mid + 1;
    }
    if (r >= 1 && Max(left, r) == Min(left, r))
        return r;
    else
        return 0;
}

int n;

signed main()
{
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &b[i]);
    for (int i = 1; i <= n; i++)
        mmax[i][0] = a[i];
    for (int i = 1; i <= n; i++)
        mmin[i][0] = b[i];
    for (int k = 1; (1 << k) <= n; k++)
        for (int i = 1; i + (1 << k) - 1 <= n; i++)
            mmax[i][k] = max(mmax[i][k - 1], mmax[i + (1 << (k - 1))][k - 1]);
    for (int k = 1; (1 << k) <= n; k++)
        for (int i = 1; i + (1 << k) - 1 <= n; i++)
            mmin[i][k] = min(mmin[i][k - 1], mmin[i + (1 << (k - 1))][k - 1]);
    int cnt = 0;
    for (int i = 1; i <= n; i++)
    {
        int l = findl(i, n, n);
        int r = findr(i, n, n);
        if (l != 0 && r != 0)
        {
            if (l > r)
                swap(l, r);
            int ans = (r - l + 1);
            cnt += ans;
        }
    }
    printf("%lld\n", cnt);
    return 0;
}

本文來自部落格園,作者:蒟蒻orz,轉載請註明原文連結:https://www.cnblogs.com/orzz/p/15489288.html