Problem:
Score : 800 points

Problem Statement

There are N cards placed on a grid with H rows and W columns of squares.

The i-th card has an integer A_i written on it, and it is placed on the square at the R_i-th row from the top and the C_i-th column from the left.

Multiple cards may be placed on the same square.

You will first pick up at most one card from each row.

Then, you will pick up at most one card from each column.

Find the maximum possible sum of the integers written on the picked cards.

Constraints

All values are integers.

1 \leq N \leq 10^5

1 \leq H, W \leq 10^5

1 \leq A_i \leq 10^5

1 \leq R_i \leq H

1 \leq C_i \leq W

Input

Input is given from Standard Input in the following format:

N H W
R_1 C_1 A_1
R_2 C_2 A_2
\vdots
R_N C_N A_N

Output

Print the maximum possible sum of the integers written on the picked cards.

Sample Input 1

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

Sample Output 1

28

The sum of the integers written on the picked cards will be 28, the maximum value possible, if you pick up cards as follows:

Pick up the fourth card from the first row.

Pick up the sixth card from the second row.

Pick up the second card from the first column.

Pick up the fifth card from the second column.

Sample Input 2

13 5 6
1 3 35902
4 6 19698
4 6 73389
3 6 3031
3 1 4771
1 4 4784
2 1 36357
2 1 24830
5 6 50219
4 6 22645
1 2 30739
1 4 68417
1 5 78537

Sample Output 2

430590

Sample Input 3

1 100000 100000
1 1 1

Sample Output 3

1

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using Debug = System.Diagnostics.Trace;
using SB = System.Text.StringBuilder;
using System.Numerics;
using static System.Math;
namespace Program {
	public class Solver {
		Random rnd = new Random();
		public void Solve() {
			var n = ri;
			var h = ri;
			var w = ri;
			var A = Enumerate(n, x => new int[] { ri - 1, h + ri - 1, ri }).OrderByDescending(x => x[2]).ToArray();
			var s = new DisjointSet(n + h + w);
			var a = new int[h + w];
			var ans = 0L;
			foreach (var x in A) {
				var p = x[0];
				var q = x[1];
				int sz = s.Same(p, q) ? s.Size(p) - a[s[p]] : s.Size(p) - a[s[p]] + s.Size(q) - a[s[q]];
				Debug.WriteLine(sz);
				if (sz > 0) {
					Debug.WriteLine(x.AsJoinedString());
					ans += x[2];
					if (s.Same(p, q)) {
						a[s[p]]++;
					}
					else {
						var u = a[s[p]] + a[s[q]] + 1;
						s.Unite(p, q);
						a[s[p]] = u;
					}
				}
			}
			Console.WriteLine(ans);
		}

		const long INF = 1L << 60;
		static int[] dx = { -1, 0, 1, 0 };
		static int[] dy = { 0, 1, 0, -1 };
		int ri { get { return sc.Integer(); } }
		long rl { get { return sc.Long(); } }
		double rd { get { return sc.Double(); } }
		string rs { get { return sc.Scan(); } }
		public IO.StreamScanner sc = new IO.StreamScanner(Console.OpenStandardInput());

		static T[] Enumerate<T>(int n, Func<int, T> f) {
			var a = new T[n];
			for (int i = 0; i < a.Length; ++i) a[i] = f(i);
			return a;
		}
		static public void Swap<T>(ref T a, ref T b) { var tmp = a; a = b; b = tmp; }
	}
}

#region main
static class Ex {
	static public string AsString(this IEnumerable<char> ie) { return new string(ie.ToArray()); }
	static public string AsJoinedString<T>(this IEnumerable<T> ie, string st = " ") {
		return string.Join(st, ie);
	}
	static public void Main() {
		Console.SetOut(new Program.IO.Printer(Console.OpenStandardOutput()) { AutoFlush = true });
		var solver = new Program.Solver();
		/* 
		var t = new System.Threading.Thread(solver.Solve, 50000000);
		t.Start();
		t.Join();
		*/
		solver.Solve();
		Console.Out.Flush();
	}
}
#endregion
#region Ex
namespace Program.IO {
	using System.IO;
	using System.Text;
	using System.Globalization;

	public class Printer : StreamWriter {
		public override IFormatProvider FormatProvider { get { return CultureInfo.InvariantCulture; } }
		public Printer(Stream stream) : base(stream, new UTF8Encoding(false, true)) { }
	}

	public class StreamScanner {
		public StreamScanner(Stream stream) { str = stream; }

		public readonly Stream str;
		private readonly byte[] buf = new byte[1024];
		private int len, ptr;
		public bool isEof = false;
		public bool IsEndOfStream { get { return isEof; } }

		private byte read() {
			if (isEof) return 0;
			if (ptr >= len) {
				ptr = 0;
				if ((len = str.Read(buf, 0, 1024)) <= 0) {
					isEof = true;
					return 0;
				}
			}
			return buf[ptr++];
		}

		public char Char() {
			byte b = 0;
			do b = read(); while ((b < 33 || 126 < b) && !isEof);
			return (char)b;
		}
		public string Scan() {
			var sb = new StringBuilder();
			for (var b = Char(); b >= 33 && b <= 126; b = (char)read()) sb.Append(b);
			return sb.ToString();
		}
		public string ScanLine() {
			var sb = new StringBuilder();
			for (var b = Char(); b != '\n' && b != 0; b = (char)read()) if (b != '\r') sb.Append(b);
			return sb.ToString();
		}
		public long Long() { return isEof ? long.MinValue : long.Parse(Scan()); }
		public int Integer() { return isEof ? int.MinValue : int.Parse(Scan()); }
		public double Double() { return isEof ? double.NaN : double.Parse(Scan(), CultureInfo.InvariantCulture); }
	}
}

#endregion

#region Disjoint Set
/// <summary>
/// いくつかの要素を素集合に分割して保持するコレクションです．
/// </summary>
public class DisjointSet {
	int[] par;
	byte[] rank;
	/// <summary>
	/// 0 から <paramref name="N"/>-1 までの番号がついた <paramref name="N"/> 個の要素からなる集合を作成します．
	/// </summary>
	/// <param name="N">要素数</param>
	/// <remarks>このコンストラクタは O(N) で実行されます．</remarks>
	public DisjointSet(int N) {
		par = new int[N];
		rank = new byte[N];
		for (int i = 0; i < N; i++)
			par[i] = -1;
	}
	/// <summary>
	/// 指定した要素が属する集合の代表値を取得します．
	/// </summary>
	/// <param name="id">調べたい要素の 0-indexed での番号</param>
	/// <returns>指定した要素が属する集合の代表値</returns>
	/// <remarks>最悪計算量 O(α(N)) で実行されます．</remarks>
	public int this[int id] {
		get {
			if ((par[id] < 0)) return id;
			return par[id] = this[par[id]];
		}
	}
	/// <summary>
	/// 指定した 2 つの要素が属する集合同士を 1 つに統合することを試みます．
	/// </summary>
	/// <param name="x">最初の要素の 0-indexed での番号</param>
	/// <param name="y">2 つ目の要素の 0-indexed での番号</param>
	/// <returns>統合に成功したならば true，そうでなければ false．</returns>
	/// <remarks>最悪計算量 O(α(N)) で実行されます．</remarks>
	public bool Unite(int x, int y) {
		x = this[x]; y = this[y];
		if (x == y) return false;
		if (rank[x] < rank[y]) { var tmp = x; x = y; y = tmp; }
		par[x] += par[y];
		par[y] = x;
		if (rank[x] == rank[y])
			rank[x]++;
		return true;
	}
	/// <summary>
	/// 指定した要素が属する集合のサイズを取得します．
	/// </summary>
	/// <param name="x">指定する要素の 0-indexed での番号</param>
	/// <returns>集合のサイズ</returns>
	/// <remarks>最悪計算量 O(α(N)) で実行されます．</remarks>
	public int Size(int x) { return -par[this[x]]; }

	/// <summary>
	/// 指定した 2 つの要素が属する集合が同じかどうかを判定する．計算量 O(α(N))．
	/// </summary>
	/// <param name="x">最初の要素の 0-indexed での番号</param>
	/// <param name="y">2 つ目の要素の 0-indexed での番号</param>
	/// <returns>同じならば true，そうでなければ false．</returns>
	/// <remarks>最悪計算量 O(α(N)) で実行されます．</remarks>
	public bool Same(int x, int y) { return this[x] == this[y]; }

}
#endregion