Problem:
Score : 300 points

Problem Statement

The season for Snuke Festival has come again this year. First of all, Ringo will perform a ritual to summon Snuke. For the ritual, he needs an altar, which consists of three parts, one in each of the three categories: upper, middle and lower.

He has N parts for each of the three categories. The size of the i-th upper part is A_i, the size of the i-th middle part is B_i, and the size of the i-th lower part is C_i.

To build an altar, the size of the middle part must be strictly greater than that of the upper part, and the size of the lower part must be strictly greater than that of the middle part. On the other hand, any three parts that satisfy these conditions can be combined to form an altar.

How many different altars can Ringo build? Here, two altars are considered different when at least one of the three parts used is different.

Constraints

1 \leq N \leq 10^5

1 \leq A_i \leq 10^9(1\leq i\leq N)

1 \leq B_i \leq 10^9(1\leq i\leq N)

1 \leq C_i \leq 10^9(1\leq i\leq N)

All input values are integers.

Input

Input is given from Standard Input in the following format:

N
A_1 ... A_N
B_1 ... B_N
C_1 ... C_N

Output

Print the number of different altars that Ringo can build.

Sample Input 1

2
1 5
2 4
3 6

Sample Output 1

3

The following three altars can be built:

Upper: 1-st part, Middle: 1-st part, Lower: 1-st part

Upper: 1-st part, Middle: 1-st part, Lower: 2-nd part

Upper: 1-st part, Middle: 2-nd part, Lower: 2-nd part

Sample Input 2

3
1 1 1
2 2 2
3 3 3

Sample Output 2

27

Sample Input 3

6
3 14 159 2 6 53
58 9 79 323 84 6
2643 383 2 79 50 288

Sample Output 3

87

Solution in C# (Mono-mcs 6.8.0.105) (judge verdict: Accepted)

using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Threading.Tasks;

/*
より単純な解法（解説通り）
各Bについて、それより小さいAの個数と、それより大きいCの個数を計算し、
掛け合わせれば良い。
*/


class Program
{
	//Main
	static void Main(string[] args)
	{
		//読み込む
		int N = int.Parse(Console.ReadLine());
		int[] An = Console.ReadLine().Split(' ').Select(i => int.Parse(i)).ToArray();
		int[] Bn = Console.ReadLine().Split(' ').Select(i => int.Parse(i)).ToArray();
		int[] Cn = Console.ReadLine().Split(' ').Select(i => int.Parse(i)).ToArray();

		//ソート
		Array.Sort(An);
		Array.Sort(Bn);
		Array.Sort(Cn);

		//最大で(10^5)^3通り有り得るので、注意！
		long ans = 0;

		//各Bnに対して、組み合わせることができるAnの個数とCnの個数とを掛け合わせる
		for(int i=0; i<N; i++){
			//Biより小さくなる最大のAi
			int	idxA = BinarySearch.Array.LowerBound<int>(An, Bn[i]) - 1;
			if(idxA < 0){
				continue;
			}

			//Biより大きくなる最小のCi
			int idxC = BinarySearch.Array.UpperBound<int>(Cn, Bn[i]);
			if(idxC >= N){
				continue;
			}

			//組み合わせ
			long combi = (long)(idxA+1) * (long)(N - idxC);
			ans += combi;
		}

		//出力
		Console.WriteLine(ans);
	}
}


/*
二分探索用の名前空間
*/
namespace BinarySearch{
	//対象が配列の場合
	class Array
	{
		//LowerBound
		/*
		引数は、対象の配列と、対象の値
		当該値以上になる最初のインデックスを返す
		*/
		public static int LowerBound<T>(T[] arr, T target)
			where T : IComparable
		{
			//長さを取得
			int len = arr.Length;

			//両端を初期化
			int max = len-1;
			int min = 0;

			//二分探索（条件を満たすまで探索）
			while(max >= min){
				//中央を更新
				int mid = min + (max-min)/2;

				//midより大きい場合（target > arr[mid]）
				/*
				cf. ここがLowerとUpperとの違い
				*/
				if(target.CompareTo(arr[mid]) > 0){
						min = mid + 1;
				}
				//小さい場合
				else{
						max = mid - 1;
				}
			}

			//target以上となる最初のインデックスを出力
			return min;
		}

		//UpperBound
		/*
		引数は、対象の配列と、対象の値
		当該値より大きくなる最初のインデックスを返す
		*/
		public static int UpperBound<T>(T[] arr, T target)
			where T : IComparable
		{
			//長さを取得
			int len = arr.Length;

			//両端を初期化
			int max = len-1;
			int min = 0;

			//二分探索（条件を満たすまで探索）
			while(max >= min){
				//中央を更新
				int mid = min + (max-min)/2;

				//mid以上の場合（target >= arr[mid]）
				/*
				cf. ここがLowerとUpperとの違い
				*/
				if(target.CompareTo(arr[mid]) >= 0){
						min = mid + 1;
				}
				//小さい場合
				else{
						max = mid - 1;
				}
			}

			//targetより大きくなる最初のインデックスを出力
			return min;
		}
	}

	//対象がリストの場合
	class List{
		//LowerBound
		/*
		引数は、対象のリストと、対象の値
		当該値以上になる最初のインデックスを返す
		*/
		public static int LowerBound<T>(List<T> list, T target)
			where T : IComparable
		{
			//長さを取得
			int len = list.Count();

			//両端を初期化
			int max = len-1;
			int min = 0;

			//二分探索（条件を満たすまで探索）
			while(max >= min){
				//中央を更新
				int mid = min + (max-min)/2;

				//midより大きい場合（target > list[mid]）
				/*
				cf. ここがLowerとUpperとの違い
				*/
				if(target.CompareTo(list[mid]) > 0){
						min = mid + 1;
				}
				//小さい場合
				else{
						max = mid - 1;
				}
			}

			//target以上となる最初のインデックスを出力
			return min;
		}

		//UpperBound
		/*
		引数は、対象のリストと、対象の値
		当該値より大きくなる最初のインデックスを返す
		*/
		public static int UpperBound<T>(List<T> list, T target)
			where T : IComparable
		{
			//長さを取得
			int len = list.Count();

			//両端を初期化
			int max = len-1;
			int min = 0;

			//二分探索（条件を満たすまで探索）
			while(max >= min){
				//中央を更新
				int mid = min + (max-min)/2;

				//mid以上の場合（target >= list[mid]）
				/*
				cf. ここがLowerとUpperとの違い
				*/
				if(target.CompareTo(list[mid]) >= 0){
						min = mid + 1;
				}
				//小さい場合
				else{
						max = mid - 1;
				}
			}

			//targetより大きくなる最初のインデックスを出力
			return min;
		}
	}
}