「Tyvj1432」 楼兰图腾 - 树状数组

在楼兰古城的壁画中,隐藏着古代部落崇拜的秘密。本篇解析如何通过编程找出壁画上代表尖刀和铁锹图腾的三元组数量,运用权值树状数组或权值线段树进行高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

在完成了分配任务之后,西部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_nyn111NNN的一个排列。

西部314打算研究这幅壁画中包含着多少个图腾,其中VVV图腾的定义如下(注意:图腾的形式只和这三个纵坐标的相对大小排列顺序有关)1≤i&lt;j&lt;k≤n1\le i&lt;j&lt;k\le n1i<j<knyi&gt;yj,yj&lt;yky_i&gt;y_j,y_j&lt;y_kyi>yj,yj<yk

而崇拜∧的部落的图腾被定义为≤i&lt;j&lt;k≤n\le i&lt;j&lt;k\le ni<j<knyi&lt;yj,yj&gt;yky_i&lt;y_j,y_j&gt;y_kyi<yj,yj>yk

西部314想知道,这nnn个点中两个部落图腾的数目。因此,你需要编写一个程序来求出VVV的个数和∧∧的个数。

输入格式

第一行一个数nnn

第二行是nnn个数,分别代表y1,y2,⋯&ThinSpace;,yny1,y2,\cdots,yny1,y2,,yn

输出格式

两个数,中间用空格隔开,依次为VVV的个数和∧∧的个数。

数据范围

10%10\%10%的数据n≤600n\le600n600

40%40\%40%的数据n≤5000n\le5000n5000

100%100\%100%的数据n≤200000n\le200000n200000,答案不超过int64int64int64

分析

首先要理解题意,就是要求一个序列中满足中间比两边大和中间比两边小的三元组个数。可以想到,若一个序列中的数aia_iai,在iii之前有ppp个数比它小,在iii之后有qqq个数比它小,则根据乘法原理,答案中∧∧的个数就要加上p∗qp*qpq,然后问题就转变为查找在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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值