这道题可以简化为:
一条直线上有N个点,每一个点有一个weight权值,对于情况:从左到右有三个点a, b, c满足a >= b > =c或者a <= b < =c。满足情况的有多少种可能。 (因为满足每个点均不相同,所以也可以是 a > b > c 或者a < b < c);
constraints:3 <= N <= 20000;
考虑dp的方法,每一个点存放两个数字,up和down分别表示到这个点升和降的个数。那么有:
1.c[i]存放权值
2. dp[i] = up[i] + down[i];
3.up[i] = ∑(up[j]) (j < i, c[i] >= c[j]); down[i] = ∑ (down[j]) ( j < i, c[j] >= c[i]);
具体复杂度O(n^2) ≈ 400000000理论上会超时,考虑优化的方法。
其实对于某一个数字c[i]来说,如果满足c[i] < c[j]而且,i < j;如果有k > j,而且c[k] > c[j],那么一定有c[k] > c[i],就做了重复的判断和加减。所以还可以更加优化复杂度。
对于a < b < c,上面考虑的是枚举c的情况,如果枚举b,可以将问题拆分成 a < b 和 b < c两个问题;
例如a[i] < a[6] < a[j],那么就需要统计:a[0] ~a[5]中满足a[i] < a[6]的个数和a[7] ~a[n]中满足a[j] > a[6]的个数。
分别考虑一下几种数据结构:
1. 线段树。(这个不太懂为什么可以。)
2.树状数组。(树状数组a数组里存放的是每一个点的权值,的c数组里存放的是满足条件的个数);
3. 块状链表。
4.平衡二叉树。
首先学习一下标程:
#include <iostream>
#include <time.h>
using namespace std;
const int maxm = 100000;//A[i]的最大值
const int maxn = 20000;//N的范围
int lb[maxm + 100], b[maxm + 100], c[maxm + 100];//lb为树状数组的辅助函数,b数组为保存了树状数组的统计信息
int a[maxn + 100];
int g[maxn + 100], h[maxn + 100];
int calc(int b[maxm], int k)//树状数组统计个数的函数{
int i , ans = 0;
for(i = k; i > 0; i -= lb[i]) ans += b[i];
return ans;
}
void ins(int b[maxm], int k){
int;
for(i = k; i <= maxm; i += lb[i] ) b[i]++;
}
int main() {
int cn, n, i;
_int64 ans;
scanf("%d", &cn);
for(i = 1; i <= maxm; i++) lb[i] = i & (-i );
while(cn--) {
scanf("%d", &n);
for(i = 0; i < n; i++) scanf("%d", &a[i]);
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
for(i = 0; i < n; i++) {
g[i] = cal(b, a[i] - 1);
ins(b, a[i]);
for(i = n - 1; i >= 0; i--) {
h[i] = calc(c, a[i] - 1);
ins(c, a[i]);
}
ans = 0;
for(i = 0; i < n; i++)
ans += (_int64)g[i] * (_int64)(n - i - 1 - h[i]) + (_int64)(i - g[i]) * (_int64)h[i];
printf("%d", ans);
}
return 0;
}
看完程序我发现树状数组存的是标志位的信息,也就是说树状数组最后的那一行是不存在的。
ac代码:
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long LL;
const int maxm = 100000;//保存A[i]的信息
const int maxn = 20000;//保存数组c给定序列的信息
int c[maxn + 100];//保存题目中的序列
int u[maxm + 100];//保存树状数组的信息,up
int d[maxm + 100];//保存树状数组的信息,down
int lb[maxm + 100];//保存lowbit的信息
int sum_u[maxn + 100];//保存up和的信息
int sum_d[maxn + 100];//保存down和的信息
void insert(int a[maxm], int k) {
int i = k;
while(i <= maxm) {
a[i] += 1;
i += lb[i];
}
}
//数组从1开始
int query(int a[maxm], int k) {
int i = k, ret = 0;
while(i > 0) {
ret += a[i];
i -= lb[i];
}
return ret;
}
void debug() {
for(int i = 1; i <= 10; i++) {
cout << i << " " << sum_u[i] << " " << sum_d[i] << " " << endl;
}
cout << endl;
//for()
}
int main() {
int T, n;
long long ans;
for(int i = 1; i <= maxm; i++) lb[i] = i & (-i);
scanf("%d", &T);
while(T--) {
ans = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &c[i]);
memset(u, 0, sizeof(u));
memset(d, 0, sizeof(d));
for(int i = 0; i < n; i++) {
sum_u[i] = query(u, c[i] - 1);//从左往右数比c[i]小的个数
insert(u, c[i]);
}
for(int i = n - 1; i >= 0; i--) {
sum_d[i] = query(d, c[i] - 1);//从右往左数比c[i]小的个数
insert(d, c[i]);
}
//cout << ans << endl;
//debug();
for(int i = 0; i < n; i++) {
ans += (LL)sum_u[i] * (n - i - 1 - sum_d[i]);
ans += (LL)(i - sum_u[i]) * sum_d[i];
//debug_begin
//int tp = (LL)sum_u[i] * (n - i - 1 - sum_d[i]) + (LL)(i - sum_d[i]) * sum_d[i];
//cout << " &&& " << i << " " << (LL)sum_u[i] * (n - i - 1 - sum_d[i]) << " " << (LL)(i - sum_d[i]) * sum_d[i] << endl;
}
printf("%I64d\n", ans);
}
return 0;
}