题目描述
在完成了分配任务之后,西部314来到了楼兰古城的西部。
相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(‘V’),一个部落崇拜铁锹(‘∧’),他们分别用V和∧的形状来代表各自部落的图腾。
西部314在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了N个点,经测量发现这N个点的水平位置和竖直位置是两两不同的。
西部314认为这幅壁画所包含的信息与这N个点的相对位置有关,因此不妨设坐标分别为(1,y1),(2,y2),…,(n,yn),其中y1~yn是1到n的一个排列。
西部314打算研究这幅壁画中包含着多少个图腾。
如果三个点(i,yi),(j,yj),(k,yk)满足1≤i[HTML_REMOVED]yj,yj<yk,则称这三个点构成V图腾;
如果三个点(i,yi),(j,yj),(k,yk)满足1≤i[HTML_REMOVED]yk,则称这三个点构成∧图腾;
西部314想知道,这n个点中两个部落图腾的数目。
因此,你需要编写一个程序来求出V的个数和∧的个数。
输入格式
第一行一个数n。
第二行是n个数,分别代表y1,y2,…,yn。
输出格式
两个数,中间用空格隔开,依次为V的个数和∧的个数。
数据范围
对于所有数据,n≤200000,且输出答案不会超过int64。
y1∼yn 是 1 到 n 的一个排列。
输入样例:
5
1 5 3 2 4
输出样例:
3 4
解
设输入为num数组(从1开始计数),sum(n)为前缀和,add(x, y)为给x增加y,初始状态树状数组置零
根据树状数组的性质,如果从左往右扫描输入,设当前输入为t,那么sum(t)就是左边比t小的数的个数,然后add(t, 1)使得t被记录…然后i - sum(t) - 1就是左边比t大的的数的个数。
反过来从右往左扫描num数组的话就会发现sum(t)是右边比t小的数的个数,n - i - sum(t)是右边比t大的数的个数。
对于每个点来说,能形成^的个数是左边比该点小的数的个数乘以右边比该点小的数的个数,能形成v的个数是左边比该点大的数的个数乘以右边比该点大的数的个数。
所以说我们只需要扫描两遍输入,并且只保留一遍的sum(t)就可以完成计算。
代码
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x & -x)
#define int long long
const int N = 200010;
int a[N], num[N], ll[N], n;
void add(int x, int y) {
for (; x <= n; x += lowbit(x))a[x] += y;
}
int sum(int x) {
int res = 0;
for (; x > 0; x -= lowbit(x))res += a[x];
return res;
}
signed main() {
cin >> n;
int resa = 0, resb = 0;
for (int i = 1; i <= n; i++) {
cin >> num[i];
int l = sum(num[i]); // 左边有多少比该点小的
ll[i] = l;
add(num[i], 1);
}
memset(a, 0, sizeof a);
for (int i = n; i; i--) {
int l = sum(num[i]); // 右边有多少比该点小的
resa += ll[i] * l;
resb += (i - ll[i] - 1) * (n - i - l);
add(num[i], 1);
}
cout << resb << " " << resa << endl;
}