CF689D Friends and Subsequences
题目大意
给定两个大小为 的序列 ,求有多少个数对 ,满足 且
,。
解题思路
ST
+ 二分。
首先考虑一个 算法,不难想到,可以 预处理 ST
表,然后暴力枚举 看是否满足要求。
我们把问题转化一下,固定左界 ,往右不难发现不考虑极端情况越靠近 , 越会小于 ,越靠近 , 越会大于 ,在 中会存在一个区间 使得这个区间里的数满足要求。
不难发现这个东西是单调的满足单调,所以直接二分 和 并判断是否满足要求,预处理可以用 ST
表完成,然后让答案加上 即可。
时间复杂度 。
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/18122107