【题解】B - 三元上升子序列

题目内容

原题:洛谷P1637

Description

Erwin 最近对一种叫thair的东西巨感兴趣。。。
在含有 \(n\) 个整数的序列\(a_1,a_2,\ldots,a_n\) 中,三个数被称作thair当且仅当 \(i\lt j\lt k\)\(a_i\lt a_j\lt a_k\)
求一个序列中 thair 的个数。

Input

开始一行一个正整数 \(n\)
以后一行 \(n\) 个整数 \(a_1,a_2,\ldots,a_n\)

Output

一行一个整数表示thair的个数。

数据规模与约定

  • 对于 \(30\%\) 的数据 保证 \(n\le100\)
  • 对于 \(60\%\) 的数据 保证 \(n\le2000\)
  • 对于 \(100\%\) 的数据 保证 \(1\le n\le3\times10^4\)\(1\le a_i\le 10^5\)

思路

我们可以通过统计以 \(a_i\) 开头的、以 \(a_i\) 结尾的、以 \(a_i\) 作为中间点的thair个数来统计答案。而在这三种方法里,无疑是最后一种最好实现。对于一个 \(a_i\),我们只需要统计满足 \(j<i\And a_j<a_i\)\(j\) 的个数 \(k_1\),满足 \(j>i\And a_j>a_i\)\(j\) 的个数 \(k_2\),相乘即可得到 \(a_i\) 对于答案的贡献。对于每个 \(a_i\),分开算它们的 \(k_1\)\(k_2\)
这里以算 \(k_1\) 为例来介绍实现步骤。从前往后遍历,每到一个位置就把这个值加入一个支持单点修改、查询前缀和的数据结构,数据结构开在值域上,加数就把对应位置的值 \(+1\),找答案就直接查询前缀和即可。由于 \(j\) 严格小于 \(i\) 所以要先统计在把当前位置的数加入数据结构(当然,由于 \(a_j\) 严格小于 \(a_i\),所以不注意顺序也无所谓)。\(k_2\) 同理。
至于使用的数据结构,可以是树状数组、线段树或值域分块(好像还有CDQ分治)。这里给出值域分块的解法,复杂度 \(O(n\sqrt{n})\)。其余算法的复杂度为 \(O(n\log n)\)

代码

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define inf 0x3f3f3f3f
int a,b[30003],c[30003];
long long ans;
struct Block_Array//块状数组封装
{
	#define N 100001
	#define M 404
	int cnt,len,lt[M],rt[M],num[M],col[N],be[N];
	il void build(int x)//分块
	{
		len=sqrt(x);
		cnt=x/len;
		fill(col+1,col+1+x,0);
		fill(num+1,num+1+cnt,0);
		for(ri i=1;i<=cnt;i++)
		{
			lt[i]=rt[i-1]+1;
			rt[i]=rt[i-1]+len;
		}
		if(rt[cnt]!=x)
		{
			cnt++;
			lt[cnt]=rt[cnt-1]+1;
			rt[cnt]=x;
		}
		for(ri i=1;i<=cnt;i++)
		{
			for(ri j=lt[i];j<=rt[i];j++)
			{
				be[j]=i;
			}
		}
	}
	il void clear(int x)//为了省时间所以反向之前清空存值数组而不是重新分块
	{
		fill(col+1,col+1+x,0);
		fill(num+1,num+1+cnt,0);
	}
	il void add(int x)//加值
	{
		col[x]++;
		num[be[x]]++;
	}
	il int find(int x,int y)//区间和查询
	{
		if(x>y)
		{
			return 0;
		}
		ri rn=0;
		if(be[x]==be[y])
		{
			for(ri i=x;i<=y;i++)
			{
				rn+=col[i];
			}
		}
		else
		{
			for(ri i=x;i<=rt[be[x]];i++)
			{
				rn+=col[i];
			}
			for(ri i=be[x]+1;i<=be[y]-1;i++)
			{
				rn+=num[i];
			}
			for(ri i=lt[be[y]];i<=y;i++)
			{
				rn+=col[i];
			}
		}
		return rn;
	}
	#undef N
	#undef M
}ba;
int main()
{
	scanf("%d",&a);
	ba.build(100000);
	for(ri i=1;i<=a;i++)
	{
		scanf("%d",&b[i]);
		c[i]=ba.find(1,b[i]-1);
		ba.add(b[i]);
	}
	ba.clear(100000);
	for(ri i=a;i>=1;i--)
	{
		ans+=c[i]*ba.find(b[i]+1,100000);
		ba.add(b[i]);
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2024-10-04 17:06  一位很会的教授er~  阅读(26)  评论(0编辑  收藏  举报