P1637 三元上升子序列 (权值线段树+乘法原理)

题目链接:link

题目大意:

Erwin 最近对一种叫thair的东西巨感兴趣。在含有 n 个整数的序列 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,,an 中,三个数被称作thair当且仅当 i < j < k 且 a i < a j < a k a_i<a_j<a_k ai<aj<ak,求一个序列中 thair的个数。

解题思路:

对于三元组,首先应该想到枚举中间的元素 ,用L[ i ]记录a[ i ]左边小于a[ i ]的元素个数,用R[ i ]记录a[ i ]右边大于a[ i ]的元素个数。
根据乘法原理有: 以a[ i ]为中间元素的合法子序列个数为L[ i ] * R[ i ]。
对于L和R数组可以用树状数组或者线段树求解,在这里写一下线段树的做法。
拓展: 对于三元子序列问题,不管是上升子序列,下降子序列,V 型或者 型都可以用这种方法来做。

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 3e4 + 10;
ll a[N], L[N], R[N];
vector<ll> alls;
struct node
{
	int l, r;
	int sum;
}tr[N << 2];
void pushup(int u)
{
	tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int l, int r, int u = 1)
{
	tr[u] = { l,r,0 };
	if (l == r) return;
	int mid = l + r >> 1;
	build(l, mid, u << 1); build(mid + 1, r, u << 1 | 1);
	pushup(u);
}
void modify(int x, int u = 1)
{
	if (tr[u].l == tr[u].r) tr[u].sum++;
	else
	{
		int mid = tr[u].l + tr[u].r >> 1;
		if (x <= mid) modify(x, u << 1);
		else modify(x, u << 1 | 1);
		pushup(u);
	}
}
int query(int l, int r, int u = 1)
{
	if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
	int mid = tr[u].l + tr[u].r >> 1;
	int ans = 0;
	if (l <= mid) ans = query(l, r, u << 1);
	if (r > mid) ans += query(l, r, u << 1 | 1);
	return ans;
}
int main()
{
	int n; cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		alls.push_back(a[i]);
	}
	sort(alls.begin(), alls.end());
	alls.erase(unique(alls.begin(), alls.end()), alls.end());
	build(1, n);
	for (int i = 1; i <= n; i++)
	{
		int k = lower_bound(alls.begin(), alls.end(), a[i]) - alls.begin() + 1;
		L[i] = query(1, k - 1);
		modify(k);
	}
	build(1, n);
	for (int i = n; i >= 1; i--)
	{
		int k = lower_bound(alls.begin(), alls.end(), a[i]) - alls.begin() + 1;
		R[i] = query(k + 1, n);
		modify(k);
	}
	ll ans = 0;
	for (int i = 1; i <= n; i++)ans += L[i] * R[i];
	cout << ans << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值