Description
样例输入:
4
1 4 3 2
样例输出:
3
Solution
对每个数字 , 可以将其在二维平面上表示
如图得到 与其他数字的 位置 与 值 的大小关系
- 第一象限 的数字与 构成以 为 第二项的 顺序对
- 第二象限 的数字与 构成以 为 第二项的 逆序对
- 第三象限 的数字与 构成以 为 第一项的 逆序对
- 第四象限 的数字与 构成以 为 第一项的 顺序对
每个数字都有 四个象限数字数量 的信息,
树状数组 维护各象限的 数字数量, 时间复杂度
接下来是 计算答案 的方法
与 是顺序对, 数量为 , 与 是逆序对, 数量为 .
, 为总方案数量, 包括不合法方案 ( 重复的方案) .
在使用 逐个减去 不合法方案 :
- , 此时 为 第四象限, 为 第三象限 .
- , 此时 为 第四象限, 为 第二象限 .
- , 此时 为 第一象限, 为 第三象限 .
- , 此时 为 第一象限, 为 第二象限 .
对于 (), 需要减去
- [ () () ]
- […]
- […]
- […]
其中 表示左, 右, 表示大, 小;
合起来共同表示符合条件的数字数量
统计答案 时间复杂度
最后 即是答案
Addition
Q: 为什么这样减去 不合法方案 会达到不重不漏的效果?
A: 这是因为每次减去的 不合法方案 都因 当前编号 重复而不合法,
在另一个位置时, 由于位置编号的唯一, 导致不合法的原因 一定不相同,
所以 不重不漏
Code
#include<bits/stdc++.h>
#define reg register
int read(){
int s = 0, flag = 1;
char c;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 1e5 + 50;
typedef long long ll;
int N;
int S[maxn];
int B[maxn];
int T[maxn];
int Lu[maxn], Ld[maxn];
int Ru[maxn], Rd[maxn];
void Modify(int k, int x){ while(k <= N) T[k] += x, k += k&-k; }
int Query(int k){ int s = 0; while(k > 0) s += T[k], k -= k&-k; return s; }
int Query_2(int l, int r){ return Query(r) - Query(l-1); }
int main(){
N = read();
for(reg int i = 1; i <= N; i ++) S[i] = read(), B[i] = S[i];
std::sort(B+1, B+N+1);
int Len = std::unique(B+1, B+N+1) - B-1;
for(reg int i = 1; i <= N; i ++) S[i] = std::lower_bound(B+1, B+Len+1, S[i]) - B;
for(reg int i = 1; i <= N; i ++){
Ld[i] = Query_2(1, S[i]-1);
Lu[i] = Query_2(S[i]+1, Len);
Modify(S[i], 1);
}
memset(T, 0, sizeof T);
for(reg int i = N; i >= 1; i --){
Rd[i] = Query_2(1, S[i]-1);
Ru[i] = Query_2(S[i]+1, Len);
Modify(S[i], 1);
}
ll sgn_num = 0, ord_num = 0, illegal_num = 0;
for(reg int i = 1; i <= N; i ++){
sgn_num += Ru[i], ord_num += Rd[i];
illegal_num += 1ll*Ru[i]*Rd[i] + 1ll*Ru[i]*Lu[i];
illegal_num += 1ll*Ld[i]*Rd[i] + 1ll*Ld[i]*Lu[i];
}
printf("%lld
", sgn_num*ord_num - illegal_num);
return 0;
}