6565
6666<!-- solution:start -->
6767
68- ### 方法一:树状数组或线段树
68+ ### 方法一:树状数组
6969
7070对于本题,我们先用 pos 记录每个数在 nums2 中的位置,然后依次对 nums1 中的每个元素进行处理。
7171
@@ -79,27 +79,14 @@ tags:
79791 . ...
80801 . 最后是 2,此时 nums2 中出现情况为 ` [4,1,0,2,3] ` ,2 之前有值的个数是 4,2 之后没有值的个数是 0。因此以 2 为中间数字能形成 0 个好三元组。
8181
82- 我们可以用** 树状数组** 或** 线段树** 这两种数据结构来更新 nums2 中各个位置数字的出现情况,快速算出每个数字左侧 1 的个数,以及右侧 0 的个数。
83-
84- ** 数据结构 1:树状数组**
82+ 我们可以用** 树状数组** 来更新 nums2 中各个位置数字的出现情况,快速算出每个数字左侧 1 的个数,以及右侧 0 的个数。
8583
8684树状数组,也称作“二叉索引树”(Binary Indexed Tree)或 Fenwick 树。 它可以高效地实现如下两个操作:
8785
88861 . ** 单点更新** ` update(x, delta) ` : 把序列 x 位置的数加上一个值 delta;
89871 . ** 前缀和查询** ` query(x) ` :查询序列 ` [1,...x] ` 区间的区间和,即位置 x 的前缀和。
9088
91- 这两个操作的时间复杂度均为 $O(\log n)$。
92-
93- ** 数据结构 2:线段树**
94-
95- 线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 ` log(width) ` 。更新某个元素的值,只需要更新 ` log(width) ` 个区间,并且这些区间都包含在一个包含该元素的大区间内。
96-
97- - 线段树的每个节点代表一个区间;
98- - 线段树具有唯一的根节点,代表的区间是整个统计范围,如 ` [1, N] ` ;
99- - 线段树的每个叶子节点代表一个长度为 1 的元区间 ` [x, x] ` ;
100- - 对于每个内部节点 ` [l, r] ` ,它的左儿子是 ` [l, mid] ` ,右儿子是 ` [mid + 1, r] ` , 其中 ` mid = ⌊(l + r) / 2⌋ ` (即向下取整)。
101-
102- > 本题 Python3 线段树代码 TLE。
89+ 这两个操作的时间复杂度均为 $O(\log n)$。因此,整体的时间复杂度为 $O(n \log n)$,其中 $n$ 为数组 $\textit{nums1}$ 的长度。空间复杂度 $O(n)$。
10390
10491<!-- tabs:start -->
10592
@@ -302,20 +289,87 @@ func goodTriplets(nums1 []int, nums2 []int) int64 {
302289}
303290```
304291
292+ #### TypeScript
293+
294+ ``` ts
295+ class BinaryIndexedTree {
296+ private c: number [];
297+ private n: number ;
298+
299+ constructor (n : number ) {
300+ this .n = n ;
301+ this .c = Array (n + 1 ).fill (0 );
302+ }
303+
304+ private static lowbit(x : number ): number {
305+ return x & - x ;
306+ }
307+
308+ update(x : number , delta : number ): void {
309+ while (x <= this .n ) {
310+ this .c [x ] += delta ;
311+ x += BinaryIndexedTree .lowbit (x );
312+ }
313+ }
314+
315+ query(x : number ): number {
316+ let s = 0 ;
317+ while (x > 0 ) {
318+ s += this .c [x ];
319+ x -= BinaryIndexedTree .lowbit (x );
320+ }
321+ return s ;
322+ }
323+ }
324+
325+ function goodTriplets(nums1 : number [], nums2 : number []): number {
326+ const n = nums1 .length ;
327+ const pos = new Map <number , number >();
328+ nums2 .forEach ((v , i ) => pos .set (v , i + 1 ));
329+
330+ const tree = new BinaryIndexedTree (n );
331+ let ans = 0 ;
332+
333+ for (const num of nums1 ) {
334+ const p = pos .get (num )! ;
335+ const left = tree .query (p );
336+ const total = tree .query (n );
337+ const right = n - p - (total - left );
338+ ans += left * right ;
339+ tree .update (p , 1 );
340+ }
341+
342+ return ans ;
343+ }
344+ ```
345+
305346<!-- tabs:end -->
306347
307348<!-- solution:end -->
308349
309350<!-- solution:start -->
310351
311- ### 方法二
352+ ### 方法二:线段树
353+
354+ 我们也可以用线段树来实现。线段树是一种数据结构,能够高效地进行区间查询和更新操作。它的基本思想是将一个区间划分为多个子区间,并且每个子区间都可以用一个节点来表示。
355+
356+ 线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 ` log(width) ` 。更新某个元素的值,只需要更新 ` log(width) ` 个区间,并且这些区间都包含在一个包含该元素的大区间内。
357+
358+ - 线段树的每个节点代表一个区间;
359+ - 线段树具有唯一的根节点,代表的区间是整个统计范围,如 ` [1, N] ` ;
360+ - 线段树的每个叶子节点代表一个长度为 1 的元区间 ` [x, x] ` ;
361+ - 对于每个内部节点 ` [l, r] ` ,它的左儿子是 ` [l, mid] ` ,右儿子是 ` [mid + 1, r] ` , 其中 ` mid = ⌊(l + r) / 2⌋ ` (即向下取整)。
362+
363+ 时间复杂度 $O(n \log n)$,其中 $n$ 为数组 $\textit{nums1}$ 的长度。空间复杂度 $O(n)$。
312364
313365<!-- tabs:start -->
314366
315367#### Python3
316368
317369``` python
318370class Node :
371+ __slots__ = (" l" , " r" , " v" )
372+
319373 def __init__ (self ):
320374 self .l = 0
321375 self .r = 0
@@ -539,6 +593,174 @@ public:
539593};
540594```
541595
596+ #### Go
597+
598+ ```go
599+ type Node struct {
600+ l, r, v int
601+ }
602+
603+ type SegmentTree struct {
604+ tr []Node
605+ }
606+
607+ func NewSegmentTree(n int) *SegmentTree {
608+ tr := make([]Node, 4*n)
609+ st := &SegmentTree{tr: tr}
610+ st.build(1, 1, n)
611+ return st
612+ }
613+
614+ func (st *SegmentTree) build(u, l, r int) {
615+ st.tr[u].l = l
616+ st.tr[u].r = r
617+ if l == r {
618+ return
619+ }
620+ mid := (l + r) >> 1
621+ st.build(u<<1, l, mid)
622+ st.build(u<<1|1, mid+1, r)
623+ }
624+
625+ func (st *SegmentTree) modify(u, x, v int) {
626+ if st.tr[u].l == x && st.tr[u].r == x {
627+ st.tr[u].v += v
628+ return
629+ }
630+ mid := (st.tr[u].l + st.tr[u].r) >> 1
631+ if x <= mid {
632+ st.modify(u<<1, x, v)
633+ } else {
634+ st.modify(u<<1|1, x, v)
635+ }
636+ st.pushup(u)
637+ }
638+
639+ func (st *SegmentTree) pushup(u int) {
640+ st.tr[u].v = st.tr[u<<1].v + st.tr[u<<1|1].v
641+ }
642+
643+ func (st *SegmentTree) query(u, l, r int) int {
644+ if st.tr[u].l >= l && st.tr[u].r <= r {
645+ return st.tr[u].v
646+ }
647+ mid := (st.tr[u].l + st.tr[u].r) >> 1
648+ res := 0
649+ if l <= mid {
650+ res += st.query(u<<1, l, r)
651+ }
652+ if r > mid {
653+ res += st.query(u<<1|1, l, r)
654+ }
655+ return res
656+ }
657+
658+ func goodTriplets(nums1 []int, nums2 []int) int64 {
659+ n := len(nums1)
660+ pos := make(map[int]int)
661+ for i, v := range nums2 {
662+ pos[v] = i + 1
663+ }
664+
665+ tree := NewSegmentTree(n)
666+ var ans int64
667+
668+ for _, num := range nums1 {
669+ p := pos[num]
670+ left := tree.query(1, 1, p)
671+ right := n - p - (tree.query(1, 1, n) - tree.query(1, 1, p))
672+ ans += int64(left * right)
673+ tree.modify(1, p, 1)
674+ }
675+
676+ return ans
677+ }
678+ ```
679+
680+ #### TypeScript
681+
682+ ``` ts
683+ class Node {
684+ l: number = 0 ;
685+ r: number = 0 ;
686+ v: number = 0 ;
687+ }
688+
689+ class SegmentTree {
690+ private tr: Node [];
691+
692+ constructor (n : number ) {
693+ this .tr = Array (4 * n );
694+ for (let i = 0 ; i < 4 * n ; i ++ ) {
695+ this .tr [i ] = new Node ();
696+ }
697+ this .build (1 , 1 , n );
698+ }
699+
700+ private build(u : number , l : number , r : number ): void {
701+ this .tr [u ].l = l ;
702+ this .tr [u ].r = r ;
703+ if (l === r ) return ;
704+ const mid = (l + r ) >> 1 ;
705+ this .build (u << 1 , l , mid );
706+ this .build ((u << 1 ) | 1 , mid + 1 , r );
707+ }
708+
709+ modify(u : number , x : number , v : number ): void {
710+ if (this .tr [u ].l === x && this .tr [u ].r === x ) {
711+ this .tr [u ].v += v ;
712+ return ;
713+ }
714+ const mid = (this .tr [u ].l + this .tr [u ].r ) >> 1 ;
715+ if (x <= mid ) {
716+ this .modify (u << 1 , x , v );
717+ } else {
718+ this .modify ((u << 1 ) | 1 , x , v );
719+ }
720+ this .pushup (u );
721+ }
722+
723+ private pushup(u : number ): void {
724+ this .tr [u ].v = this .tr [u << 1 ].v + this .tr [(u << 1 ) | 1 ].v ;
725+ }
726+
727+ query(u : number , l : number , r : number ): number {
728+ if (this .tr [u ].l >= l && this .tr [u ].r <= r ) {
729+ return this .tr [u ].v ;
730+ }
731+ const mid = (this .tr [u ].l + this .tr [u ].r ) >> 1 ;
732+ let res = 0 ;
733+ if (l <= mid ) {
734+ res += this .query (u << 1 , l , r );
735+ }
736+ if (r > mid ) {
737+ res += this .query ((u << 1 ) | 1 , l , r );
738+ }
739+ return res ;
740+ }
741+ }
742+
743+ function goodTriplets(nums1 : number [], nums2 : number []): number {
744+ const n = nums1 .length ;
745+ const pos = new Map <number , number >();
746+ nums2 .forEach ((v , i ) => pos .set (v , i + 1 ));
747+
748+ const tree = new SegmentTree (n );
749+ let ans = 0 ;
750+
751+ for (const num of nums1 ) {
752+ const p = pos .get (num )! ;
753+ const left = tree .query (1 , 1 , p );
754+ const total = tree .query (1 , 1 , n );
755+ const right = n - p - (total - left );
756+ ans += left * right ;
757+ tree .modify (1 , p , 1 );
758+ }
759+
760+ return ans ;
761+ }
762+ ```
763+
542764<!-- tabs:end -->
543765
544766<!-- solution:end -->
0 commit comments