AcWing 241. 楼兰图腾
在完成了分配任务之后,西部 314来到了楼兰古城的西部。
相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V
),一个部落崇拜铁锹(∧
),他们分别用 V
和 ∧
的形状来代表各自部落的图腾。
西部 314 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n 个点,经测量发现这 n 个点的水平位置和竖直位置是两两不同的。
西部 314 认为这幅壁画所包含的信息与这 n 个点的相对位置有关,因此不妨设坐标分别为 ( 1 , y 1 ) , ( 2 , y 2 ) , … , ( n , y n ) (1,y1),(2,y_2),\dots,(n,y_n) (1,y1),(2,y2),…,(n,yn),其中 y 1 ∼ y n y_1∼y_n y1∼yn 是 1 到 n 的一个排列。
西部 314 打算研究这幅壁画中包含着多少个图腾。
如果三个点
(
i
,
y
i
)
,
(
j
,
y
j
)
,
(
k
,
y
k
)
(i,y_i),(j,y_j),(k,y_k)
(i,yi),(j,yj),(k,yk) 满足
1
≤
i
<
j
<
k
≤
n
1≤i<j<k≤n
1≤i<j<k≤n 且
y
i
>
y
j
,
y
j
<
y
k
y_i>y_j,y_j<y_k
yi>yj,yj<yk ,则称这三个点构成 V
图腾;
如果三个点
(
i
,
y
i
)
,
(
j
,
y
j
)
,
(
k
,
y
k
)
(i,y_i),(j,y_j),(k,y_k)
(i,yi),(j,yj),(k,yk) 满足
1
≤
i
<
j
<
k
≤
n
1≤i<j<k≤n
1≤i<j<k≤n 且
y
i
<
y
j
,
y
j
>
y
k
y_i<y_j,y_j>y_k
yi<yj,yj>yk ,则称这三个点构成 ∧
图腾;
西部 314 想知道,这 n 个点中两个部落图腾的数目。
因此,你需要编写一个程序来求出 V
的个数和 ∧
的个数。
输入格式
第一行一个数 n。
第二行是 n 个数,分别代表 y 1 , y 2 , … , y n y1,y2,…,yn y1,y2,…,yn。
输出格式
两个数,中间用空格隔开,依次为 V
的个数和 ∧
的个数。
数据范围
对于所有数据,
n
≤
2
∗
1
0
5
n≤2*10^5
n≤2∗105,且输出答案不会超过 int64。
y
1
∼
y
n
y1∼yn
y1∼yn 是 1 到 n 的一个排列。
输入样例:
5
1 5 3 2 4
输出样例:
3 4
#include<bits/stdc++.h>
#include <unordered_map>
using namespace std;
template<class...Args>
void debug(Args... args) {//Parameter pack
auto tmp = { (cout << args << ' ', 0)... };
cout << "\n";
}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>pll;
typedef pair<int, int>pii;
const ll N = 1e6 + 5;
const ll MOD = 1e9 + 7;
const ll INF = 0x7fffffff;
const ll maxv = N;//区间最大值
ll Binary_Indexed_Tree[N];//树状数组
ll lowbit(ll x) {//数状数组lowbit()
return x & -x;
}
void update(ll index, ll val) {//在index位置加上val
while (index <= maxv) {
Binary_Indexed_Tree[index] += val;
index += lowbit(index);
}
}
ll sum(ll index) {//求Binary_Indexed_Tree[1 , index]的和
ll ans = 0;
while (index) {
ans += Binary_Indexed_Tree[index];
index -= lowbit(index);
}
return ans;
}
ll a[N];
ll L_big[N], L_small[N], R_big[N], R_small[N];
int main() {
ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {//计算左区间
//顺序遍历,已加入树状数组的点必然出现在现加入的点的左边的位置
//加入的是对应位置的值,所以在树状数组中左边是小于他的元素右边是大于它的元素
L_small[i] = sum(a[i]);
L_big[i] = sum(n) - sum(a[i]);
update(a[i], 1);
}
memset(Binary_Indexed_Tree, 0, sizeof(Binary_Indexed_Tree));
for (int i = n; i >= 1; i--) {//计算右区间
//逆序遍历,已加入树状数组的点必然出现在现加入的点的右边的位置
//加入的是对应位置的值,所以在树状数组中左边是小于他的元素右边是大于它的元素
R_small[i] = (sum(n) - sum(a[i]));
R_big[i] = sum(a[i]);
update(a[i], 1);
}
ll ansA = 0, ansV = 0;
for (int i = 1; i <= n; i++) {
//以某j为基础j左边所有比他大的数乘以它右边比他小的数为V的个数
ansV += L_big[i] * R_small[i];
ansA += L_small[i] * R_big[i];
}
cout << ansV << " " << ansA;
return 0;
}