【luogu P4755】Beautiful Pair(ST表)(笛卡尔树)(主席树)

Beautiful Pair

题目链接:luogu P4755

题目大意

给你一个数组,问你有多少个区间 [l,r] 满足 l 位置上的值乘 r 位置上的值小于等于区间中的最大值。

思路

看到求最大值,你考虑把每个数可以作为最大值掌控的范围求出来,分别解决。

这个可以通过建立笛卡尔树来解决,不过我们不用真的建树,可以用 ST 表每次得到最大值,然后把两边递归下去解决即可。

接着考虑每个最大值怎么算贡献。
考虑暴力枚举一边,另外一遍的区间建出值域线段树,然后就可以在另一边查询。
但是暴力枚举可能会 T,而且你建线段树显然是不能枚举的。

首先解决暴力枚举的问题,不难发现用启发式合并,暴力枚举小的部分即可。
至于至于线段树,观察到时一段区间,考虑用主席树实现。

然后就好啦。

代码

#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;

int n, a[100001], log_2[100001], rt[100001];
int maxn[100001][21], pl[100001][21], tot;
int b[100001], nn;
ll ans;

struct XD_tree {//主席树
	ll val[100001 << 6];
	int ls[100001 << 6], rs[100001 << 6];
	
	int copy(int x) {
		int now = ++tot;
		val[now] = val[x]; ls[now] = ls[x]; rs[now] = rs[x];
		return now;
	}
	
	int insert(int now, int l, int r, int pl) {
		now = copy(now);
		val[now]++;
		if (l == r) return now;
		int mid = (l + r) >> 1;
		if (pl <= mid) ls[now] = insert(ls[now], l, mid, pl);
			else rs[now] = insert(rs[now], mid + 1, r, pl);
		return now;
	}
	
	ll query(int now, int l, int r, int L, int R) {
		if (!now) return 0;
		if (L > R) return 0;
		if (L <= l && r <= R) return val[now];
		int mid = (l + r) >> 1;
		ll re = 0;
		if (L <= mid) re += query(ls[now], l, mid, L, R);
		if (mid < R) re += query(rs[now], mid + 1, r, L, R);
		return re;
	}
}T;

int get_pl(int x) {
	return lower_bound(b + 1, b + nn + 1, x) - b;
}

int get_pl_(int x) {
	return (upper_bound(b + 1, b + nn + 1, x) - b) - 1;
}

int get_max(int l, int r) {
	int k = log_2[r - l + 1];
	if (maxn[l][k] > maxn[r - (1 << k) + 1][k]) return pl[l][k];
		else return pl[r - (1 << k) + 1][k];
}

void dfs(int l, int r) {
	if (l > r) return ;
	if (l == r) {
		if (a[l] == 1) ans++;
		return ;
	}
	int mid = get_max(l, r);//ST表找最大值的位置
	if (mid - l + 1 > r - mid) {//启发式合并
		for (int i = mid; i <= r; i++) {
			ans += T.query(rt[mid], 1, nn, 1, get_pl_(a[mid] / a[i]));
			ans -= T.query(rt[l - 1], 1, nn, 1, get_pl_(a[mid] / a[i]));
		}
	}
	else {
		for (int i = l; i <= mid; i++) {
			ans += T.query(rt[r], 1, nn, 1, get_pl_(a[mid] / a[i]));
			ans -= T.query(rt[mid - 1], 1, nn, 1, get_pl_(a[mid] / a[i]));
		}
	}
	dfs(l, mid - 1); dfs(mid + 1, r);//递归处理分开的区间
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]), maxn[i][0] = a[i], pl[i][0] = i, b[i] = a[i];
	
	sort(b + 1, b + n + 1);
	nn = unique(b + 1, b + n + 1) - b - 1;
	
	log_2[0] = -1;//ST表预处理
	for (int i = 1; i <= n; i++) log_2[i] = log_2[i >> 1] + 1;
	
	for (int i = 1; i <= 20; i++)
		for (int j = 1; j + (1 << i) - 1 <= n; j++) {
			if (maxn[j][i - 1] > maxn[j + (1 << (i - 1))][i - 1]) pl[j][i] = pl[j][i - 1];
				else pl[j][i] = pl[j + (1 << (i - 1))][i - 1];
			maxn[j][i] = max(maxn[j][i - 1], maxn[j + (1 << (i - 1))][i - 1]);
		}
	
	for (int i = 1; i <= n; i++)
		rt[i] = T.insert(rt[i - 1], 1, nn, get_pl(a[i]));
	dfs(1, n);
	
	printf("%lld", ans);
	
	return 0;
}
原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P4755.html