三元上升子序列

题意:
给定一个长度为n的数列\(a_1, a_2, a_3...a_n\),三个数可以被称为三元上升子序列,当且仅当\(i < j < k\)\(a_i < a_j < a_k\)

其实只要是三元,不管题目是要求\(a_i < a_j < a_k\)还是\(a_i > a_j > a_k\)还是\(a_i < a_j > a_K\)还是\(a_i > a_j < a_k\)还是要求可以取等号,都可用同一思路做的啦。
大概就是我们以中间那个数(即\(a_j\))为单位,分别统计在它前面比它小的,和在它后面比它大的,然后乘一下就得到以\(a_j\)为中间那个数的三元上升子序列个数。于是我们枚举每一个数作为中间数考虑,然后把得到的数累加就可以得到答案了。
为了降低复杂度,我们先从前往后枚举一遍,依次把数加入树状数组,加入它之前就查询一下当前树状数组中比它小的有几个。(这里如果不能取等的话,在加入之前还是之后查询都无所谓,如果可以取等号,就要在加入之前查询)
然后再从后往前枚举一遍,同样依次把数加入树状数组,加入每个数之前就查询一下当前树状数组中比它大的有几个。
然后对于每个数所记录的两个数据相乘再累加即可。

这里的两次枚举,就是为了保证数字下标之间的大小关系,而树状数组则是用来保证数字大小之间的数量关系,明确这一点之后思路应该就很清晰了。
代码的话emmm……下次再补吧

2020.9.22 update:

#include<bits/stdc++.h>
using namespace std;
#define Ri register int
#define AC 60000
#define lowbit(x) (x & (-x))
#define LL long long

int n; LL ans;
int tree[AC], be[AC], af[AC];
struct node{int id, num, w;}s[AC];

inline bool cmp(node a, node b) {return a.num < b.num;}
inline bool cmp2(node a, node b) {return a.id < b.id;}

inline int read()
{
	int x = 0; char ch = getchar(); bool z = false;
	while((ch > '9' || ch < '0') && ch != '-') ch = getchar();
	if(ch == '-') z = true, ch = getchar();
	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	return z ? -x : x;
}

void pre()
{
	n = read();
	for(Ri i = 1; i <= n; i ++) s[i].id = i, s[i].num = read();
	sort(s + 1, s + n + 1, cmp);
	int now = 0;
	for(Ri i = 1; i <= n; i ++) 
	{
		++ now, s[i].w = now;
		while(s[i].num == s[i + 1].num) s[++i].w = now;
	}
	sort(s + 1, s + n + 1, cmp2);
}

int cnt(int x)
{
	int now = 0;
	for(Ri i = x; i; i -= lowbit(i)) now += tree[i];
	return now;
}

void add(int x) 
{
	if(!x) return ;
	for(Ri i = x; i <= n; i += lowbit(i)) tree[i] ++;
}

void clear(){for(Ri i = 1; i <= n; i ++) tree[i] = 0;}

void work()
{
	for(Ri i = 1; i <= n; i ++) be[i] = cnt(s[i].w - 1), add(s[i].w);
	clear();
	for(Ri i = n; i; i --) af[i] = n - i - cnt(s[i].w), add(s[i].w);
	for(Ri i = 1; i <= n; i ++) ans += 1LL* be[i] * af[i]; 
	printf("%lld\n", ans);
	/*for(Ri i = 1; i <= n; i ++) printf("%d ", be[i]);
	printf("\n");
	for(Ri i = 1; i <= n; i ++) printf("%d ", af[i]);
	printf("\n");*/
}

int main()
{
//	freopen("1.in", "r", stdin);
	pre();	
	work();
//	fclose(stdin);
	return 0;
}

写了离散化,后面调用的时候用了原数值……然后成功wa了两次,老年退役且痴呆选手好难

posted @ 2020-09-11 23:23  ww3113306  阅读(234)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。