题意:
给定一个长度为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
", ans);
/*for(Ri i = 1; i <= n; i ++) printf("%d ", be[i]);
printf("
");
for(Ri i = 1; i <= n; i ++) printf("%d ", af[i]);
printf("
");*/
}
int main()
{
// freopen("1.in", "r", stdin);
pre();
work();
// fclose(stdin);
return 0;
}
写了离散化,后面调用的时候用了原数值……然后成功wa了两次,老年退役且痴呆选手好难