AcWing Problem 241 - 楼兰图腾
题目类型:树状数组 + 标记
题意
分别统计这样的三元组的个数:
1.(
y
a
y_a
ya,
y
b
y_b
yb,
y
c
y_c
yc)其中
y
a
>
y
b
y_a > y_b
ya>yb 、
y
b
<
y
c
y_b<y_c
yb<yc 、
0
<
a
<
b
<
c
≤
n
0 < a < b < c \leq n
0<a<b<c≤n
2.(
y
a
y_a
ya,
y
b
y_b
yb,
y
c
y_c
yc)其中
y
a
<
y
b
y_a<y_b
ya<yb 、
y
b
>
y
c
y_b> y_c
yb>yc 、
0
<
a
<
b
<
c
≤
n
0 < a < b < c \leq n
0<a<b<c≤n
分析
对于第一种情况每个数贡献的个数为它 左边比它大的数乘右边比它大的数。左边比它大的数以及右边比它大的数可以预处理出前缀和后缀然后每次 O ( 1 ) O(1) O(1) 获得。因为数据是 1~n 的排列,所以可以使用树状数组进行标记,从 1 到 n 开始遍历数组,当遍历一个数后将其添加进树状数组中 a [ i ] a[i] a[i] 位置标记为 1,那么树状数组对 a [ i ] a[i] a[i] 求得前缀和就是它左边比它小的数,然后用对 n 求得前缀和减去比它小的数的个数就得到比它大的个数,这样结束后就可以求得前缀。后缀可以通过反向遍历数组用同样的方式得到。总时间复杂度 O ( n log n + n ) O(n\logn+n) O(nlogn+n)
代码
static int[] a;
static int[] C;
public static void solve() throws IOException {
int n = nextInt();
init(n);
for (int i = 1; i <= n; i++) a[i] = nextInt();
int[] greater = new int[n + 1];
int[] lower = new int[n + 1];
for (int i = 1; i <= n; i++) {
greater[i] = query(n) - query(a[i]);
lower[i] = query(a[i] - 1);
add(a[i], 1, n);
}
Arrays.fill(C, 0);
long ans1 = 0, ans2 = 0;
for (int i = n; i >= 1; i--) {
ans1 += (long) greater[i] * (query(n) - query(a[i]));
ans2 += (long) lower[i] * query(a[i] - 1);
add(a[i], 1, n);
}
pw.println(ans1 + " " + ans2);
}
public static void add(int x, int v, int n) {
while (x <= n) {
C[x] += v;
x += lowbit(x);
}
}
public static int query(int x) {
int re = 0;
while (x > 0) {
re += C[x];
x -= lowbit(x);
}
return re;
}
public static int lowbit(int x) { return x & (-x); }
public static void init(int n) {
a = new int[n + 1];
C = new int[n + 1];
}
/*******************************************************************************************/
/*******************************************************************************************/
// Fast I/O