Problem:
Score : 600 points

Problem Statement

We have N colored balls arranged in a row from left to right; the color of the i-th ball from the left is c_i.

You are given Q queries. The i-th query is as follows: how many different colors do the l_i-th through r_i-th balls from the left have?

Constraints

1\leq N,Q \leq 5 \times 10^5

1\leq c_i \leq N

1\leq l_i \leq r_i \leq N

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N Q
c_1 c_2 \cdots c_N
l_1 r_1
l_2 r_2
:
l_Q r_Q

Output

Print Q lines. The i-th line should contain the response to the i-th query.

Sample Input 1

4 3
1 2 1 3
1 3
2 4
3 3

Sample Output 1

2
3
1

The 1-st, 2-nd, and 3-rd balls from the left have the colors 1, 2, and 1 - two different colors.

The 2-st, 3-rd, and 4-th balls from the left have the colors 2, 1, and 3 - three different colors.

The 3-rd ball from the left has the color 1 - just one color.

Sample Input 2

10 10
2 5 6 5 2 1 7 9 7 2
5 5
2 4
6 7
2 2
7 8
7 9
1 8
6 9
8 10
6 8

Sample Output 2

1
2
2
1
2
2
6
3
3
3

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.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;

public class Main {
	
	public static void main(String[] args) throws IOException {
		MyScanner sc = new MyScanner();
		PrintWriter out = new PrintWriter(System.out);
		int N = sc.nextInt();
		int Q = sc.nextInt();
		int[] C = new int[N];
		for (int i = 0; i < N; i++) {
			C[i] = sc.nextInt() - 1;
		}
		List<Segment>[] queries = new List[N];
		for (int i = 0; i < N; i++) {
			queries[i] = new ArrayList<>();
		}
		for (int i = 0; i < Q; i++) {
			int l = sc.nextInt() - 1;
			int r = sc.nextInt() - 1;
			queries[r].add(new Segment(l, r, i));
		}
		int[] prev = new int[N];
		Arrays.fill(prev, -1);
		int[] ans = new int[Q];
		
		SumSegmentTree tree = new SumSegmentTree(N);
		for (int i = 0; i < N; i++) {
			tree.add(i, 1);
			if (prev[C[i]] != -1) {
				tree.add(prev[C[i]], -1);
			}
			prev[C[i]] = i;
			for (Segment segment : queries[i]) {
				ans[segment.idx] = (int)tree.getSum(segment.from, i + 1);
			}
		}
		
		for (int an : ans) {
			out.println(an);
		}
		
		out.flush();
	}
	
	static class Segment {
		int from, to, idx;
		
		public Segment(int from, int to, int idx) {
			this.from = from;
			this.to = to;
			this.idx = idx;
		}
	}
	
	static class SumSegmentTree {
		public int size;
		public long[] values;  // sum of subtree
		
		/**
		 * @param capacity for queries in range [0, capacity)
		 */
		public SumSegmentTree(int capacity) {
			size = 1;
			while (size < capacity) size *= 2;
			values = new long[2 * size];
		}
		
		public void add(int position, long value) {
			add(position,1, 0, size, value);
		}
		
		private void add(int position, int idx, int idxFrom, int idxTo, long value) {
			if (idxTo <= position || position < idxFrom) return;
			if (position == idxFrom && idxTo == position + 1) {
				values[idx] += value;
				return;
			}
			int med = (idxFrom + idxTo) / 2;
			add(position, 2*idx, idxFrom, med, value);
			add(position, 2*idx+1, med, idxTo, value);
			values[idx] = values[2*idx] + values[2*idx+1];
		}
		
		public long getSum(int fromInclusive, int toExclusive) {
			return getSum(fromInclusive, toExclusive, 1, 0, size);
		}
		
		private long getSum(int fromInclusive, int toExclusive, int idx, int idxFrom, int idxTo) {
			if (idxTo <= fromInclusive || toExclusive <= idxFrom) return 0;
			if (fromInclusive <= idxFrom && idxTo <= toExclusive) {
				return values[idx];
			}
			// Previous conditions => not a leaf
			int med = (idxFrom + idxTo) / 2;
			long ret = getSum(fromInclusive, toExclusive, 2*idx, idxFrom, med);
			ret += getSum(fromInclusive, toExclusive, 2*idx+1, med, idxTo);
			return ret;
		}
	}
	
	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());
		}
	}
}