树形数据结构之树状基础-算法赛
今天给分享的是一道算法决赛的题目,这道题目的综合要求比较高,希望大家可以好好理解,同时这道题用到的是树状树形结构的有关知识。可以用这几天学的相关内容结合起来。
问题描述
给定两个长度为 N的排列 A 和 B。若一对二元组下标 (i,j) 满足以下关系则被称之为压制二元组:
- 1≤i<j≤N。
- pAi<pAj,其中 px 表示值 x在数组 B 中的下标。
一对压制二元组的价值被定义为 j−ij−i,请你计算出所有压制二元组的价值之和。
排列的定义:长度为 N 的排列表示一个长度为 NN 的数组,其中 [1,N][1,N] 每个数都恰好出现一次。
输入格式
第一行输入一个整数 N(1≤N≤2×105)N(1≤N≤2×105) 表示排列的长度。
第二行输入 N 个整数A1,A2,A3,⋯,AN 表示排列 A。
第三行输入 N 个整数B1,B2,B3,⋯,BN 表示排列 B。
保证 A,B 是一个排列。
输出格式
输出一个整数表示答案。
输入案例:
4
2 4 1 3
4 1 2 3
输出案例:
7
说明
样例中有效的压制二元组有 1,4),(2,3),(2,4),(3,4),总价值为 4−1+3−2+4−2+4−3=7。
注意:二元组指的是下标对。
代码答案:
#include<iostream>
using namespace std;
typedef long long LL;
const int N = 200010;
int n;
int a[N],h[N]; // h[]存储每个数在B数组中的下标位置
LL b[N],c[N]; // b[]数组存储数量,c[]数组存储下标之和int lowbit(int x)
{return x & -x;
}void update(int pos,int x,LL d[])
{for(int i = pos;i <= n;i+=lowbit(i))d[i] += x;
}LL query(int pos,LL d[])
{LL res = 0;for(int i = pos;i;i-=lowbit(i)) res += d[i];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++){int x;scanf("%d",&x);h[x] = i;}LL ans = 0;for(int i = 1;i<=n;i++){update(h[a[i]],1,b);update(h[a[i]],i,c);ans += i * query(h[a[i]] - 1,b) - query(h[a[i]] - 1,c);}printf("%lld\n",ans);return 0;
}
1.首先需要理解题目中所求的价值对应的是a中的坐标,即
对于当前元素i,所有符合条件的j(j<i且p[A[j]]<p[A[i]])的总价值是:
sum(i-j) = i * 符合条件的j的数量 - sum(符合条件的j的具体值)
2.两个树状数组b和c的作用:
b
数组:记录「在某个位置上有多少个历史元素」。- 当我们处理元素
j
时,会在b
数组中h[A[j]]
的位置加 1(表示该位置新增了一个元素)。 query(pos-1, b)
的结果是:所有「位置小于当前pos
」的历史元素的总数量(即满足p[A[j]] < p[A[i]]
的j
的个数)。
- 当我们处理元素
c
数组:记录「在某个位置上所有历史元素的下标之和」。- 当我们处理元素
j
时,会在c
数组中h[A[j]]
的位置加j
(表示该位置新增元素的下标是j
)。 query(pos-1, c)
的结果是:所有「位置小于当前pos
」的历史元素的下标总和(即满足p[A[j]] < p[A[i]]
的j
的总和)。
- 当我们处理元素
3.根据案例的具体实现流程:
步骤 1:处理i=1
(A[1]=2
)
pos = h[A[1]] = h[2] = 3
(A[1]=2
在B
中的位置是 3)。- 更新
b
和c
:在位置 3 分别加 1 和 1(i=1
)。b
数组状态:b[3] = 1
(其他位置 0)。c
数组状态:c[3] = 1
(其他位置 0)。
- 计算贡献:
query(2, b) = 0
(没有位置小于 3 的历史元素),所以ans += 1*0 - 0 = 0
。 - 当前
ans = 0
。
步骤 2:处理i=2
(A[2]=4
)
pos = h[A[2]] = h[4] = 1
(A[2]=4
在B
中的位置是 1)。- 更新
b
和c
:在位置 1 分别加 1 和 2(i=2
)。b
数组状态:b[1]=1
,b[3]=1
。c
数组状态:c[1]=2
,c[3]=1
。
- 计算贡献:
query(0, b) = 0
(没有位置小于 1 的历史元素),所以ans += 2*0 - 0 = 0
。 - 当前
ans = 0
。
步骤 3:处理i=3
(A[3]=1
)
pos = h[A[3]] = h[1] = 2
(A[3]=1
在B
中的位置是 2)。- 更新
b
和c
:在位置 2 分别加 1 和 3(i=3
)。b
数组状态:b[1]=1
,b[2]=1
,b[3]=1
。c
数组状态:c[1]=2
,c[2]=3
,c[3]=1
。
- 计算贡献:
query(1, b) = 1
(位置 1 有 1 个元素,即j=2
),query(1, c) = 2
(j=2
的下标和)。- 贡献为
3*1 - 2 = 1
,ans += 1
。
- 贡献为
- 当前
ans = 1
。
步骤 4:处理i=4
(A[4]=3
)
pos = h[A[4]] = h[3] = 4
(A[4]=3
在B
中的位置是 4)。- 更新
b
和c
:在位置 4 分别加 1 和 4(i=4
)。 - 计算贡献:
query(3, b) = 3
(位置 1、2、3 共有 3 个元素,即j=1,2,3
),query(3, c) = 2+3+1=6
(这些j
的下标和)。- 贡献为
4*3 - 6 = 6
,ans += 6
。
- 贡献为
- 当前
ans = 1 + 6 = 7
(与样例输出一致)。