CF689D Friends and Subsequences

题目大意

给定两个大小为 nn 的序列 ai,bia_i,b_i,求有多少个数对 (l,r)(l,r),满足 1lrn1\le l\le r\le n

maxi=lrai=mini=lrbi\max_{i=l}^r a_i=\min_{i=l}^r b_i

1n2×1051 \le n\le 2\times 10^5ai,bi109|a_i|,|b_i|\le 10^9

解题思路

ST + 二分。

首先考虑一个 O(n2)\mathcal{O}(n^2) 算法,不难想到,可以 O(nlogn)\mathcal O(n \log n) 预处理 ST 表,然后暴力枚举 (l,r)(l,r) 看是否满足要求。

我们把问题转化一下,固定左界 ll,往右不难发现不考虑极端情况越靠近 llmax{ai}\max\{a_i\} 越会小于 min{bi}\min\{b_i\},越靠近 nnmax{ai}\max\{a_i\} 越会大于 min{bi}\min\{b_i\},在 [l,n][l,n] 中会存在一个区间 [l,r][l',r'] 使得这个区间里的数满足要求。

不难发现这个东西是单调的满足单调,所以直接二分 ll'rr' 并判断是否满足要求,预处理可以用 ST 表完成,然后让答案加上 rl+1r'-l'+1 即可。

时间复杂度 O(nlogn+nlogn)\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;
}
posted @ 2021-10-31 13:46  蒟蒻orz  阅读(0)  评论(0编辑  收藏  举报  来源