麻了,这么多典题没做过……
思路
分治 / 笛卡尔树。
这一类和区间最值相关的区间端点对计数应该都可以用这种做法做。
由于求的是最大值,不妨从大到小考虑每个 ai 的贡献。
显然存在一个连续的区间 [li,ri],使得这个区间任意包含 i 的子区间最大值均为 ai.
所以与 ai 相关的贡献只有可能在这个区间中产生。
这道题中 li=maxj<i,aj≥aij+1,ri=minj>i,aj>aij−1,注意要特判一下边界情况。
一个思路是枚举 [li,i],[i,ri] 中较短的一个区间,尝试将当中的每一个位置作为区间一侧的端点,然后在另一侧计数满足条件的端点个数。这个过程可以用数据结构维护。
这种做法本质上是分治,等价于从大到小考虑 ai 的贡献,不断将当前的分治区间划分成两部分。
考虑这个分治的逆过程,相当于不断将两个小的合并区间合并。
因为每次遍历较小的区间,所以本质上是启发式合并,故而复杂度不超过 O(nlogn).
这道题中可以转化成询问某个区间内小于等于某个数的值个数。考虑将询问差分一下,离线下来挂在相应的位置询问就行。
还有另外一种构造笛卡尔树的做法,本质上是相同的,只不过是考虑在笛卡尔树上进行中序遍历来访问区间而已。可能这种做法比离线好调一点。
这个做法实际上是利用笛卡尔树的性质(大根堆)来求 [li,ri],可能泛用性没有分治做法高。
两种做法的时间复杂度都是 O(nlog2n)
另一道可以用这个方法解决的题目是 CF1777F
代码
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
#define pb push_back
const int maxn = 1e5 + 5;
int n;
int top, stk[maxn], idx[maxn];
int a[maxn], b[maxn];
int lft[maxn], rgt[maxn];
vector<int> val[maxn], coe[maxn];
namespace BIT
{
int c[maxn];
int lowbit(int x) { return x & (-x); }
void update(int p, int w) { for (int i = p; i <= n; i += lowbit(i)) c[i] += w; }
int query(int p) { int res = 0; for (int i = p; i; res += c[i], i -= lowbit(i)) ; return res; }
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
for (int i = 1; i <= n; i++)
{
while (top && a[i] > stk[top]) top--;
lft[i] = top ? idx[top] + 1 : 1, stk[++top] = a[i], idx[top] = i;
}
top = 0;
for (int i = n; i >= 1; i--)
{
while (top && a[i] >= stk[top]) top--;
rgt[i] = top ? idx[top] - 1 : n, stk[++top] = a[i], idx[top] = i;
}
for (int i = 1; i <= n; i++)
{
if (i - lft[i] <= rgt[i] - i)
{
val[i - 1].pb(1), coe[i - 1].pb(-1);
val[rgt[i]].pb(1), coe[rgt[i]].pb(1);
for (int j = lft[i]; j <= i - 1; j++)
{
val[i - 1].pb(a[i] / a[j]), coe[i - 1].pb(-1);
val[rgt[i]].pb(a[i] / a[j]), coe[rgt[i]].pb(1);
}
}
else
{
val[lft[i] - 1].pb(1), coe[lft[i] - 1].pb(-1);
val[i].pb(1), coe[i].pb(1);
for (int j = i + 1; j <= rgt[i]; j++)
{
val[lft[i] - 1].pb(a[i] / a[j]), coe[lft[i] - 1].pb(-1);
val[i].pb(a[i] / a[j]), coe[i].pb(1);
}
}
}
sort(b + 1, b + n + 1);
int m = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
ll ans = 0;
for (int i = 1; i <= n; i++)
{
BIT::update(a[i], 1);
for (int j = 0, v, c; j < val[i].size(); j++)
{
v = (val[i][j] >= b[m] ? m : upper_bound(b + 1, b + m + 1, val[i][j]) - b - 1), c = coe[i][j];
ans += BIT::query(v) * c;
}
}
printf("%lld\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现