题目描述
在完成了分配任务之后,西部314来到了楼兰古城的西部。相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V)(V)(V),一个部落崇拜铁锹(∧)(∧)(∧),他们分别用VVV和∧∧∧的形状来代表各自部落的图腾。
西部314在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了NNN个点,经测量发现这NNN个点的水平位置和竖直位置是两两不同的。西部314认为这幅壁画所包含的信息与这NNN个点的相对位置有关,因此不妨设坐标分别为(1,y1),(2,y2),⋯ ,(n,yn),(1,y_1),(2,y_2),\cdots,(n,y_n),(1,y1),(2,y2),⋯,(n,yn),其中y1y_1y1~yny_nyn是111到NNN的一个排列。
西部314打算研究这幅壁画中包含着多少个图腾,其中VVV图腾的定义如下(注意:图腾的形式只和这三个纵坐标的相对大小排列顺序有关)1≤i<j<k≤n1\le i<j<k\le n1≤i<j<k≤n且yi>yj,yj<yky_i>y_j,y_j<y_kyi>yj,yj<yk;
而崇拜∧的部落的图腾被定义为≤i<j<k≤n\le i<j<k\le n≤i<j<k≤n且yi<yj,yj>yky_i<y_j,y_j>y_kyi<yj,yj>yk;
西部314想知道,这nnn个点中两个部落图腾的数目。因此,你需要编写一个程序来求出VVV的个数和∧∧∧的个数。
输入格式
第一行一个数nnn;
第二行是nnn个数,分别代表y1,y2,⋯ ,yny1,y2,\cdots,yny1,y2,⋯,yn。
输出格式
两个数,中间用空格隔开,依次为VVV的个数和∧∧∧的个数。
数据范围
10%10\%10%的数据n≤600n\le600n≤600;
40%40\%40%的数据n≤5000n\le5000n≤5000;
100%100\%100%的数据n≤200000n\le200000n≤200000,答案不超过int64int64int64。
分析
首先要理解题意,就是要求一个序列中满足中间比两边大和中间比两边小的三元组个数。可以想到,若一个序列中的数aia_iai,在iii之前有ppp个数比它小,在iii之后有qqq个数比它小,则根据乘法原理,答案中∧∧∧的个数就要加上p∗qp*qp∗q,然后问题就转变为查找在iii之前比aia_iai小的数,在iii之前比aia_iai大的数,在iii之后比aia_iai小的数,在iii之后比aia_iai大的数的个数。这个东西就可以用权值树状数组或权值线段树维护。由于本题数据为111~nnn的一个排列,所以就不用离散化了。
代码
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=200005;
typedef long long LL;
int n,a[N];
LL c[N],f[N][5],ans1,ans2;
#define lowbit(x) ((x)&-(x))
void Updata(int x,int v) {
while (x<=n) {
c[x]+=v;
x+=lowbit(x);
}
}
LL Ask(int x) {
LL res=0;
while (x) {
res+=c[x];
x-=lowbit(x);
}
return res;
}
int main() {
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++) {//在i前面比a[i]小
f[i][1]=Ask(a[i]-1);
Updata(a[i],1);
}
memset(c,0,sizeof(c));
for (int i=n;i>=1;i--) {//在i后面比a[i]小
f[i][2]=Ask(a[i]-1);
Updata(a[i],1);
}
for (int i=1;i<=n;i++) a[i]=n-a[i]+1;//将大小变换,方便求大的数的个数
memset(c,0,sizeof(c));
for (int i=1;i<=n;i++) {//在i前面比现在的a[i]小的数,由于大小变换了
f[i][3]=Ask(a[i]-1);//所以相当于求i前面比原来的a[i]大的数的个数
Updata(a[i],1);
}
memset(c,0,sizeof(c));
for (int i=n;i>=1;i--) {//在i后面比原来a[i]大的数
f[i][4]=Ask(a[i]-1);
Updata(a[i],1);
}
for (int i=1;i<=n;i++) {
ans1+=(f[i][3]*f[i][4]);//枚举累加答案
ans2+=(f[i][1]*f[i][2]);
}
printf("%lld %lld",ans1,ans2);
return 0;
}