Problem:
Score : 400 points

Problem Statement

Takahashi has N sticks that are distinguishable from each other. The length of the i-th stick is L_i.

He is going to form a triangle using three of these sticks. Let a, b, and c be the lengths of the three sticks used. Here, all of the following conditions must be satisfied:

a < b + c

b < c + a

c < a + b

How many different triangles can be formed? Two triangles are considered different when there is a stick used in only one of them.

Constraints

All values in input are integers.

3 \leq N \leq 2 \times 10^3

1 \leq L_i \leq 10^3

Input

Input is given from Standard Input in the following format:

N
L_1 L_2 ... L_N

Constraints

Print the number of different triangles that can be formed.

Sample Input 1

4
3 4 2 1

Sample Output 1

1

Only one triangle can be formed: the triangle formed by the first, second, and third sticks.

Sample Input 2

3
1 1000 1

Sample Output 2

0

No triangles can be formed.

Sample Input 3

7
218 786 704 233 645 728 389

Sample Output 3

23

Solution in C# (.NET Core 3.1.201) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using static System.Math;

namespace CSharpSpiral {
	class Program {
		public static void Main(string[] args) {
			var sw = new StreamWriter(Console.OpenStandardOutput()) {
				AutoFlush=
#if DEBUG
			true
#else
			false
#endif
			};
			Console.SetOut(sw);
			new Program().Solve(new ConsoleInput(
#if DEBUG
			new StreamReader("../../../inputData.txt")
#else
			Console.In
#endif
			, ' '));
			Console.Out.Flush();
		}
		public void Solve(ConsoleInput cin) {
			var N = cin.ReadInt;
			var L = cin.ReadIntArray(N);
			var LL = new MultiSet<int>();
			var count = 0;
			L.MergeSort(false);
			foreach(var k in L) {
				LL.Insert(k);
			}
			for(int i = 0; i<N; i++) {
				for(int j = i+1; j<N; j++) {
					var kk = LL.LowerBound(L[i]+L[j]);
					kk-=j+1;
					if(kk<0) kk=0;
					count+=kk;
				}
			}
			Console.WriteLine(count);
		}

	}

	static class ExtendClass {
		public static string Join<T>(this IEnumerable<T> X, string separator = " ")
			=> string.Join(separator, X);
		#region MergeSort
		public static void MergeSort<T>(this T[] A, bool desending) where T : IComparable<T> {
			if(desending)
				MergeSort(A, Comparer<T>.Create((x, y) => y.CompareTo(x)));
			else
				MergeSort(A, Comparer<T>.Create((x, y) => x.CompareTo(y)));
		}
		public static void MergeSort<T>(this T[] A, IComparer<T> comparer) where T : IComparable<T> {
			MergeSort1(0, A.Length, A, comparer);
		}
		private static void Merge<T>(int left, int mid, int right, T[] A, IComparer<T> comparer)
			where T : IComparable<T> {
			var nl = mid-left;
			var nr = right-mid;
			var L = new T[nl];
			var R = new T[nr];
			for(int i = 0; i<nl; i++)
				L[i]=A[i+left];
			for(int i = 0; i<nr; i++)
				R[i]=A[i+mid];
			int il = 0, ir = 0;
			for(int k = left; k<right; k++) {
				if(comparer.Compare(L[il], R[ir])<=0) {
					A[k]=L[il++];
					if(il==nl) {
						k++;
						for(; ir<nr;)
							A[k++]=R[ir++];
						break;
					}
				} else {
					A[k]=R[ir++];
					if(ir==nr) {
						k++;
						for(; il<nl;)
							A[k++]=L[il++];
						break;
					}
				}
			}
		}
		private static void MergeSort1<T>(int left, int right, T[] A, IComparer<T> comparer)
		where T : IComparable<T> {
			if(left+1<right) {
				var mid = (left+right)/2;
				MergeSort1(left, mid, A, comparer);
				MergeSort1(mid, right, A, comparer);
				Merge(left, mid, right, A, comparer);
			}
		}

		#endregion
	}
	class ConsoleInput {
		private readonly TextReader _stream;
		private readonly char _separator = ' ';
		private readonly Queue<string> inputStream;
		public ConsoleInput(TextReader stream, char separator = ' ') {
			this._separator=separator;
			this._stream=stream;
			inputStream=new Queue<string>();
		}
		public string Read {
			get {
				if(inputStream.Count!=0) return inputStream.Dequeue();
				string[] tmp = _stream.ReadLine().Split(_separator);
				for(int i = 0; i<tmp.Length; ++i)
					inputStream.Enqueue(tmp[i]);
				return inputStream.Dequeue();
			}
		}
		public string ReadLine { get { return _stream.ReadLine(); } }
		public int ReadInt { get { return int.Parse(Read); } }
		public long ReadLong { get { return long.Parse(Read); } }
		public double ReadDouble { get { return double.Parse(Read); } }
		public string[] ReadStrArray(long N) {
			var ret = new string[N];
			for(long i = 0; i<N; ++i)
				ret[i]=Read;
			return ret;
		}
		public int[] ReadIntArray(long N) { var ret = new int[N]; for(long i = 0; i<N; ++i) ret[i]=ReadInt; return ret; }
		public long[] ReadLongArray(long N) { var ret = new long[N]; for(long i = 0; i<N; ++i) ret[i]=ReadLong; return ret; }
		public double[] ReadDoubleArray(long N) { var ret = new double[N]; for(long i = 0; i<N; ++i) ret[i]=ReadDouble; return ret; }
		public bool isEOF {
			get { return _stream.Peek()==-1; }
		}
	}
	//http://yambe2002.hatenablog.com/entry/2017/02/07/122421
	/// <summary>
	/// Self-Balancing Binary Search Tree
	/// (using Randamized BST)
	/// </summary>
	public class SB_BinarySearchTree<T> where T : IComparable {
		public class Node {
			public T Value;
			public Node LChild;
			public Node RChild;
			public int Count;     //size of the sub tree

			public Node(T v) {
				Value=v;
				Count=1;
			}
		}

		static Random _rnd = new Random(Seed: new Random(32222).Next());

		public static int Count(Node t) {
			return t==null ? 0 : t.Count;
		}

		static Node Update(Node t) {
			t.Count=Count(t.LChild)+Count(t.RChild)+1;
			return t;
		}

		public static Node Merge(Node l, Node r) {
			if(l==null||r==null) return l??r;

			if((double)Count(l)/(double)(Count(l)+Count(r))>_rnd.NextDouble()) {
				l.RChild=Merge(l.RChild, r);
				return Update(l);
			} else {
				r.LChild=Merge(l, r.LChild);
				return Update(r);
			}
		}

		/// <summary>
		/// split as [0, k), [k, n)
		/// </summary>
		public static Tuple<Node, Node> Split(Node t, int k) {
			if(t==null) return new Tuple<Node, Node>(null, null);
			if(k<=Count(t.LChild)) {
				var s = Split(t.LChild, k);
				t.LChild=s.Item2;
				return new Tuple<Node, Node>(s.Item1, Update(t));
			} else {
				var s = Split(t.RChild, k-Count(t.LChild)-1);
				t.RChild=s.Item1;
				return new Tuple<Node, Node>(Update(t), s.Item2);
			}
		}

		public static Node Remove(Node t, T v) {
			if(Find(t, v)==null) return t;
			return RemoveAt(t, LowerBound(t, v));
		}

		public static Node RemoveAt(Node t, int k) {
			var s = Split(t, k);
			var s2 = Split(s.Item2, 1);
			return Merge(s.Item1, s2.Item2);
		}

		public static bool Contains(Node t, T v) {
			return Find(t, v)!=null;
		}

		public static Node Find(Node t, T v) {
			while(t!=null) {
				var cmp = t.Value.CompareTo(v);
				if(cmp>0) t=t.LChild;
				else if(cmp<0) t=t.RChild;
				else break;
			}
			return t;
		}

		public static Node FindByIndex(Node t, int idx) {
			if(t==null) return null;

			var currentIdx = Count(t)-Count(t.RChild)-1;
			while(t!=null) {
				if(currentIdx==idx) return t;
				if(currentIdx>idx) {
					t=t.LChild;
					currentIdx-=(Count(t?.RChild)+1);
				} else {
					t=t.RChild;
					currentIdx+=(Count(t?.LChild)+1);
				}
			}

			return null;
		}

		public static int UpperBound(Node t, T v) {
			var torg = t;
			if(t==null) return -1;

			var ret = Int32.MaxValue;
			var idx = Count(t)-Count(t.RChild)-1;
			while(t!=null) {
				var cmp = t.Value.CompareTo(v);

				if(cmp>0) {
					ret=Math.Min(ret, idx);
					t=t.LChild;
					idx-=(Count(t?.RChild)+1);
				} else if(cmp<=0) {
					t=t.RChild;
					idx+=(Count(t?.LChild)+1);
				}
			}
			return ret==Int32.MaxValue ? Count(torg) : ret;
		}

		public static int LowerBound(Node t, T v) {
			var torg = t;
			if(t==null) return -1;

			var idx = Count(t)-Count(t.RChild)-1;
			var ret = Int32.MaxValue;
			while(t!=null) {
				var cmp = t.Value.CompareTo(v);
				if(cmp>=0) {
					if(cmp==0) ret=Math.Min(ret, idx);
					t=t.LChild;
					if(t==null) ret=Math.Min(ret, idx);
					idx-=t==null ? 0 : (Count(t.RChild)+1);
				} else if(cmp<0) {
					t=t.RChild;
					idx+=(Count(t?.LChild)+1);
					if(t==null) return idx;
				}
			}
			return ret==Int32.MaxValue ? Count(torg) : ret;
		}

		public static Node Insert(Node t, T v) {
			var ub = LowerBound(t, v);
			return InsertByIdx(t, ub, v);
		}

		static Node InsertByIdx(Node t, int k, T v) {
			var s = Split(t, k);
			return Merge(Merge(s.Item1, new Node(v)), s.Item2);
		}

		public static IEnumerable<T> Enumerate(Node t) {
			var ret = new List<T>();
			Enumerate(t, ret);
			return ret;
		}

		static void Enumerate(Node t, List<T> ret) {
			if(t==null) return;
			Enumerate(t.LChild, ret);
			ret.Add(t.Value);
			Enumerate(t.RChild, ret);
		}
	}
	/// <summary>
	/// C-like set
	/// </summary>
	public class Set<T> where T : IComparable {
		protected SB_BinarySearchTree<T>.Node _root;

		public T this[int idx] { get { return ElementAt(idx); } }

		public int Count() {
			return SB_BinarySearchTree<T>.Count(_root);
		}

		public virtual void Insert(T v) {
			if(_root==null) _root=new SB_BinarySearchTree<T>.Node(v);
			else {
				if(SB_BinarySearchTree<T>.Find(_root, v)!=null) return;
				_root=SB_BinarySearchTree<T>.Insert(_root, v);
			}
		}

		public void Clear() {
			_root=null;
		}

		public void Remove(T v) {
			_root=SB_BinarySearchTree<T>.Remove(_root, v);
		}

		public bool Contains(T v) {
			return SB_BinarySearchTree<T>.Contains(_root, v);
		}

		public T ElementAt(int k) {
			var node = SB_BinarySearchTree<T>.FindByIndex(_root, k);
			if(node==null) throw new IndexOutOfRangeException();
			return node.Value;
		}

		public int Count(T v) {
			return SB_BinarySearchTree<T>.UpperBound(_root, v)-SB_BinarySearchTree<T>.LowerBound(_root, v);
		}

		public int LowerBound(T v) {
			return SB_BinarySearchTree<T>.LowerBound(_root, v);
		}

		public int UpperBound(T v) {
			return SB_BinarySearchTree<T>.UpperBound(_root, v);
		}

		public Tuple<int, int> EqualRange(T v) {
			if(!Contains(v)) return new Tuple<int, int>(-1, -1);
			return new Tuple<int, int>(SB_BinarySearchTree<T>.LowerBound(_root, v), SB_BinarySearchTree<T>.UpperBound(_root, v)-1);
		}

		public List<T> ToList() {
			return new List<T>(SB_BinarySearchTree<T>.Enumerate(_root));
		}

		public T defaultValue { get; set; }

		public T MinOrDefault() {
			if(_root==null) return defaultValue;
			var tmp = _root;
			while(tmp.LChild!=null) {
				tmp=tmp.LChild;
			}
			return tmp.Value;
		}

		public T MaxOrDefault() {
			if(_root==null) return defaultValue;
			var tmp = _root;
			while(tmp.RChild!=null) {
				tmp=tmp.RChild;
			}
			return tmp.Value;
		}
		public bool Any() => _root!=null;

	}
	/// <summary>
	/// C-like multiset
	/// </summary>
	public class MultiSet<T>: Set<T> where T : IComparable {
		public override void Insert(T v) {
			if(_root==null) _root=new SB_BinarySearchTree<T>.Node(v);
			else _root=SB_BinarySearchTree<T>.Insert(_root, v);
		}
	}

}