Problem:
Score : 600 points

Problem Statement

You are given a forest with N vertices and M edges. The vertices are numbered 0 through N-1.
The edges are given in the format (x_i,y_i), which means that Vertex x_i and y_i are connected by an edge.

Each vertex i has a value a_i.
You want to add edges in the given forest so that the forest becomes connected.
To add an edge, you choose two different vertices i and j, then span an edge between i and j.
This operation costs a_i + a_j dollars, and afterward neither Vertex i nor j can be selected again.

Find the minimum total cost required to make the forest connected, or print Impossible if it is impossible.

Constraints

1 ≤ N ≤ 100,000

0 ≤ M ≤ N-1

1 ≤ a_i ≤ 10^9

0 ≤ x_i,y_i ≤ N-1

The given graph is a forest.

All input values are integers.

Input

Input is given from Standard Input in the following format:

N M
a_0 a_1 .. a_{N-1}
x_1 y_1
x_2 y_2
:
x_M y_M

Output

Print the minimum total cost required to make the forest connected, or print Impossible if it is impossible.

Sample Input 1

7 5
1 2 3 4 5 6 7
3 0
4 0
1 2
1 3
5 6

Sample Output 1

7

If we connect vertices 0 and 5, the graph becomes connected, for the cost of 1 + 6 = 7 dollars.

Sample Input 2

5 0
3 1 4 1 5

Sample Output 2

Impossible

We can't make the graph connected.

Sample Input 3

1 0
5

Sample Output 3

0

The graph is already connected, so we do not need to add any edges.

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Runtime Error)

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
 
public class Main{
	public static void main(String[] args){
		FastScanner sc = new FastScanner();
		int N = sc.nextInt();
		int M = sc.nextInt();
		if(M==0){
			if(N==1) System.out.println(0);
			else System.out.println("Impossible");
			return;
		}else if(M<N/2-1) System.out.println("Impossible");
		
		//グラフの作成
		long[] a = new long[N];
		int[][] edge = new int[M][2];
		for(int i=0;i<N;++i) a[i] = sc.nextLong();
		for(int i=0;i<M;++i){
			edge[i][0] = sc.nextInt();
			edge[i][1] = sc.nextInt();
		}
		
		//森を連結成分に分解し、その個数をカウントする
		int[] parent = new int[N];
		int[] rank = new int[N];
		for(int i=0;i<N;++i){
			parent[i] = i; rank[i] = 0;
		}
		for(int i=0;i<M;++i){
			int xRoot = find(edge[i][0],parent);
			int yRoot = find(edge[i][1],parent);
			if( xRoot!=yRoot ){
				if(rank[xRoot]>rank[yRoot]){
					parent[yRoot]=xRoot;
				}else if(rank[xRoot]<rank[yRoot]){
					parent[xRoot]=yRoot;
				}else{
					parent[yRoot]=xRoot;
					rank[xRoot]++;
				}
			}
		}
		for(int i=0;i<N;++i) parent[i] = find(i,parent);
		ArrayList<Integer> al = new ArrayList<Integer>();
		al.add(parent[0]);
		for(int i=1;i<N;++i){
			if(al.indexOf(parent[i])==-1) al.add(parent[i]);
		}
		int num = al.size();
		
		//(連結成分の個数)-1だけ辺を最小コストで追加する
		long ans = 0;
		boolean[] used = new boolean[N];
		int kijun = parent[0];
		for(int i=num-1;i>0;i--){
			long kouho1=Long.MAX_VALUE;
			long kouho2=Long.MAX_VALUE;
			int p1 = -1;
			int p2 = -1;
			for(int j=0;j<N;++j){
				if(!used[j]&&parent[j]==kijun&&a[j]<kouho1){
					kouho1 = a[j];
					p1 = j;
				}
				if(!used[j]&&parent[j]!=kijun&&a[j]<kouho2){
					kouho2 = a[j];
					p2 = j;
				}
			}
			ans += kouho1+kouho2;
			used[p1]=true;
			used[p2]=true;
			int kesu = parent[p2];
			for(int j=0;j<N;++j){
				if(parent[j]==kesu) parent[j] = kijun;
			}
		}
		System.out.println(ans);
		return;
	}

	public static int find(int x, int[] parent){
		if (x != parent[x]) {
            parent[x] = find(parent[x],parent);
        }
        return parent[x];
	}
}


class FastScanner {
    private final InputStream in = System.in;
    private final byte[] buffer = new byte[1024];
    private int ptr = 0;
    private int buflen = 0;
    private boolean hasNextByte() {
        if (ptr < buflen) {
            return true;
        }else{
            ptr = 0;
            try {
                buflen = in.read(buffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (buflen <= 0) {
                return false;
            }
        }
        return true;
    }
    private int readByte() { if (hasNextByte()) return buffer[ptr++]; else return -1;}
    private static boolean isPrintableChar(int c) { return 33 <= c && c <= 126;}
    public boolean hasNext() { while(hasNextByte() && !isPrintableChar(buffer[ptr])) ptr++; return hasNextByte();}
    public String next() {
        if (!hasNext()) throw new NoSuchElementException();
        StringBuilder sb = new StringBuilder();
        int b = readByte();
        while(isPrintableChar(b)) {
            sb.appendCodePoint(b);
            b = readByte();
        }
        return sb.toString();
    }
    public long nextLong() {
        if (!hasNext()) throw new NoSuchElementException();
        long n = 0;
        boolean minus = false;
        int b = readByte();
        if (b == '-') {
            minus = true;
            b = readByte();
        }
        if (b < '0' || '9' < b) {
            throw new NumberFormatException();
        }
        while(true){
            if ('0' <= b && b <= '9') {
                n *= 10;
                n += b - '0';
            }else if(b == -1 || !isPrintableChar(b)){
                return minus ? -n : n;
            }else{
                throw new NumberFormatException();
            }
            b = readByte();
        }
    }
    public int nextInt() {
        long nl = nextLong();
        if (nl < Integer.MIN_VALUE || nl > Integer.MAX_VALUE) throw new NumberFormatException();
        return (int) nl;
    }
    public double nextDouble() { return Double.parseDouble(next());}
}