Problem:
Score : 800 points

Problem Statement

Let’s take a prime P = 200\,003.
You are given N integers A_1, A_2, \ldots, A_N.
Find the sum of ((A_i \cdot A_j) \bmod P) over all N \cdot (N-1) / 2 unordered pairs of elements (i < j).

Please note that the sum isn't computed modulo P.

Constraints

2 \leq N \leq 200\,000

0 \leq A_i < P = 200\,003

All values in input are integers.

Input

Input is given from Standard Input in the following format.

N
A_1 A_2 \cdots A_N

Output

Print one integer — the sum over ((A_i \cdot A_j) \bmod P).

Sample Input 1

4
2019 0 2020 200002

Sample Output 1

474287

The non-zero products are:

2019 \cdot 2020 \bmod P = 78320

2019 \cdot 200002 \bmod P = 197984

2020 \cdot 200002 \bmod P = 197983

So the answer is 0 + 78320 + 197984 + 0 + 0 + 197983 = 474287.

Sample Input 2

5
1 1 2 2 100000

Sample Output 2

600013

Solution in Java (OpenJDK 11.0.6) (judge verdict: Accepted)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.rmi.dgc.VMID;
import java.util.StringTokenizer;

public class Main {
	static long P = 200003;
	static long root = 2;
	static long inv[] = new long[(int)P];
	static long pots[] = new long[(int)P - 1];

	public static void main(String[] args) throws IOException {
		MyScanner sc = new MyScanner();
		PrintWriter out = new PrintWriter(System.out);
		long cur = root;
		pots[0] = 1;
		for (int i = 1; i < P - 1; i++) {
			inv[(int)cur] = i;
			pots[i] = cur;
			cur *= root;
			cur %= P;
		}
		int[] got = new int[(int)P - 1];
		int N = sc.nextInt();
		for (int i = 0; i < N; i++) {
			int tmp = sc.nextInt();
			if (tmp != 0) {
				got[(int)inv[tmp]]++;
			}
		}
		FFT fft = new FFT(1 << 19);
		long[] times = fft.multiply(got, got);
		long ans = 0;
		for (int i = 0; i < times.length; i++) {
			ans += times[i] * pots[(int)(i % (P - 1))];
		}
		for (int i = 0; i < P - 1; i++) {
			ans -= (long)got[i] * pots[(int)((2 * i) % (P -1))];
		}
		out.println(ans/2);
		
		out.flush();
	}

	static class FFT {
		double[] roots;
		int maxN;

		public FFT(int maxN) {
			this.maxN = maxN;
			initRoots();
		}

		public long[] multiply(int[] a, int[] b) {
			int minSize = a.length + b.length - 1;
			int bits = 1;
			while (1 << bits < minSize) bits++;
			int N = 1<<bits;
			double[] aa = toComplex(a, N);
			double[] bb = toComplex(b, N);
			fftIterative(aa, false);
			fftIterative(bb, false);
			double[] c = new double[aa.length];
			for (int i = 0; i < N; i++) {
				c[2*i] = aa[2*i]*bb[2*i] - aa[2*i+1]*bb[2*i+1];
				c[2*i+1] = aa[2*i]*bb[2*i+1] + aa[2*i+1]*bb[2*i];
			}
			fftIterative(c, true);
			long[] ret = new long[minSize];
			for (int i = 0; i < ret.length; i++) {
				ret[i] = Math.round(c[2*i]);
			}
			return ret;
		}

		static double[] toComplex(int[] arr, int size) {
			double[] ret = new double[size * 2];
			for (int i = 0; i < arr.length; i++) {
				ret[2*i] = arr[i];
			}
			return ret;
		}

		void initRoots() {
			roots = new double[2 * (maxN + 1)];
			double ang = 2 * Math.PI / maxN;
			for (int i = 0; i <= maxN; i++) {
				roots[2 * i] = Math.cos(i * ang);
				roots[2 * i + 1] = Math.sin(i * ang);
			}
		}

		int bits(int N) {
			int ret = 0;
			while (1 << ret < N) ret++;
			if (1 << ret != N) throw new RuntimeException();
			return ret;
		}

		void fftIterative(double[] array, boolean inv) {
			int bits = bits(array.length / 2);
			int N = 1 << bits;
			for (int from = 0; from < N; from++) {
				int to = Integer.reverse(from) >>> (32 - bits);
				if (from < to) {
					double tmpR = array[2*from];
					double tmpI = array[2*from+1];
					array[2*from] = array[2*to];
					array[2*from+1] = array[2*to+1];
					array[2*to] = tmpR;
					array[2*to+1] = tmpI;
				}
			}
			for (int n = 2; n <= N; n *= 2) {
				int delta = 2 * maxN / n;
				for (int from = 0; from < N; from += n) {
					int rootIdx = inv ? 2 * maxN : 0;
					double tmpR, tmpI;
					for (int arrIdx = 2 * from; arrIdx < 2 * from + n; arrIdx += 2) {
						tmpR = array[arrIdx + n] * roots[rootIdx] - array[arrIdx + n + 1] * roots[rootIdx + 1];
						tmpI = array[arrIdx + n] * roots[rootIdx + 1] + array[arrIdx + n + 1] * roots[rootIdx];
						array[arrIdx + n] = array[arrIdx] - tmpR;
						array[arrIdx + n + 1] = array[arrIdx + 1] - tmpI;
						array[arrIdx] += tmpR;
						array[arrIdx + 1] += tmpI;
						rootIdx += (inv ? -delta : delta);
					}
				}
			}
			if (inv) {
				for (int i = 0; i < array.length; i++) {
					array[i] /= N;
				}
			}
		}
	}


	static class MyScanner {
		private BufferedReader br;
		private StringTokenizer tokenizer;
		
		public MyScanner() {
			br = new BufferedReader(new InputStreamReader(System.in));
		}
		
		public String next() {
			while (tokenizer == null || !tokenizer.hasMoreTokens()) {
				try {
					tokenizer = new StringTokenizer(br.readLine());
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
			return tokenizer.nextToken();
		}
		
		public int nextInt() {
			return Integer.parseInt(next());
		}
		
		public long nextLong() {
			return Long.parseLong(next());
		}
	}
}