楼兰图腾
在完成了分配任务之后,西部 314 来到了楼兰古城的西部。
相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V
),一个部落崇拜铁锹(∧
),他们分别用 V
和 ∧
的形状来代表各自部落的图腾。
西部 314 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n 个点,经测量发现这 n 个点的水平位置和竖直位置是两两不同的。
西部 314 认为这幅壁画所包含的信息与这 n 个点的相对位置有关,因此不妨设坐标分别为 ( 1 , y 1 ) , ( 2 , y 2 ) , … , ( n , y n ) (1,y_1),(2,y_2),…,(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
yi<y_j,y_j>y_k
yi<yj,yj>yk,则称这三个点构成 ∧
图腾;
西部 314 想知道,这 n 个点中两个部落图腾的数目。
因此,你需要编写一个程序来求出 V
的个数和 ∧
的个数。
输入格式
第一行一个数 n。
第二行是 n 个数,分别代表 y 1 , y 2 , … , y n y_1,y_2,…,y_n y1,y2,…,yn。
输出格式
两个数,中间用空格隔开,依次为 V
的个数和 ∧
的个数。
数据范围
对于所有数据,n≤200000,且输出答案不会超过 int64。
y
1
∼
y
n
y_1∼y_n
y1∼yn 是 1 到 n 的一个排列。
输入样例:
5
1 5 3 2 4
输出样例:
3 4
难度:简单
时/空限制:1s / 64MB
总通过数:3601
总尝试数:6049
来源:《算法竞赛进阶指南》
思路
对于数列中的每一个数字,计算它前面比它小的数字的数量
p
r
e
v
m
n
prevmn
prevmn,后面比它小的数字的数量
p
o
s
t
m
n
postmn
postmn,前面比它大的数字的数量
p
r
e
v
m
x
prevmx
prevmx,后面比它大的数字的数量
p
o
s
t
m
x
postmx
postmx,即可求出最终结果。
由于题目描述:y1∼yn 是 1 到 n 的一个排列
,所以只需要通过树状数组计算出
p
r
e
v
m
n
prevmn
prevmn,其余几个量都可以推导出来。
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=2e5+10;
int a[maxn],c[maxn],n,ans1,ans2;
int ask(int x){
int ret=0;
for(;x;x-=(x&-x)) ret+=c[x];
return ret;
}
void add(int x){
for(;x<maxn;x+=(x&-x)) c[x]++;
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++){
int prevmn=ask(a[i]-1),postmn=a[i]-1-prevmn;
int prevmx=i-prevmn-1,postmx=n-a[i]-prevmx;
add(a[i]);
ans1+=prevmx*postmx,ans2+=prevmn*postmn;
}
printf("%lld %lld",ans1,ans2);
}