Problem:
Score : 700 points

Problem Statement

We have a graph with N vertices and M edges, and there are two people on the graph: Takahashi and Aoki.

The i-th edge connects Vertex U_i and Vertex V_i.
The time it takes to traverse this edge is D_i minutes, regardless of direction and who traverses the edge (Takahashi or Aoki).

Takahashi departs Vertex S and Aoki departs Vertex T at the same time. Takahashi travels to Vertex T and Aoki travels to Vertex S, both in the shortest time possible.
Find the number of the pairs of ways for Takahashi and Aoki to choose their shortest paths such that they never meet (at a vertex or on an edge) during the travel, modulo 10^9 + 7.

Constraints

1 \leq N \leq 100 000

1 \leq M \leq 200 000

1 \leq S, T \leq N

S \neq T

1 \leq U_i, V_i \leq N (1 \leq i \leq M)

1 \leq D_i \leq 10^9 (1 \leq i \leq M)

If i \neq j, then (U_i, V_i) \neq (U_j, V_j) and (U_i, V_i) \neq (V_j, U_j).

U_i \neq V_i (1 \leq i \leq M)

D_i are integers.

The given graph is connected.

Input

Input is given from Standard Input in the following format:

N M
S T
U_1 V_1 D_1
U_2 V_2 D_2
:
U_M V_M D_M

Output

Print the answer.

Sample Input 1

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

Sample Output 1

2

There are two ways to choose shortest paths that satisfies the condition:

Takahashi chooses the path 1 \rightarrow 2 \rightarrow 3, and Aoki chooses the path 3 \rightarrow 4 \rightarrow 1.

Takahashi chooses the path 1 \rightarrow 4 \rightarrow 3, and Aoki chooses the path 3 \rightarrow 2 \rightarrow 1.

Sample Input 2

3 3
1 3
1 2 1
2 3 1
3 1 2

Sample Output 2

2

Sample Input 3

3 3
1 3
1 2 1
2 3 1
3 1 2

Sample Output 3

2

Sample Input 4

8 13
4 2
7 3 9
6 2 3
1 6 4
7 6 9
3 8 9
1 2 2
2 8 12
8 6 9
2 5 5
4 2 18
5 3 7
5 1 515371567
4 8 6

Sample Output 4

6

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Wrong Answer)

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.PriorityQueue;
import java.util.Scanner;
import java.util.Set;
import java.util.StringTokenizer;

public class Main {
	
	public static class State implements Comparable<State> {
		int node;
		long time;
		
		public State(int node, long time){
			this.node = node;
			this.time = time;
		}

		@Override
		public int compareTo(State o) {
			return Long.compare(this.time, o.time);
		}
	}
	
	public static int pre_count = 0;
	public static int dfs(int[] pres, int[] lows, int node, int parent, ArrayList<HashMap<Integer, Long>> adj, ArrayList<HashSet<Integer>> bridges){
		pres[node] = pre_count++;
		lows[node] = pres[node];
		//System.out.println(node + 1);
		
		for(final Entry<Integer, Long> entry : adj.get(node).entrySet()){
			final int next = entry.getKey();
			
			if(pres[next] < 0){
				lows[node] = Math.min(lows[node], dfs(pres, lows, next, node, adj, bridges));
				
				//System.out.println((node + 1) + " " + (lows[node]));
				if(lows[next] == pres[next]){
					bridges.get(node).add(next);
					bridges.get(next).add(node);
				}
			}else{
				if(next == parent){ continue; }
				lows[node] = Math.min(lows[node], lows[next]);
			}
		}
		
		return lows[node];
	}
	
	public static final long MOD = 1000000007;
	
	public static void dijkstra(final int N, int start, long[] times, long[] patterns, ArrayList<HashMap<Integer, Long>> adj){
		PriorityQueue<State> queue = new PriorityQueue<State>();
		
		Arrays.fill(times, Long.MAX_VALUE);
		Arrays.fill(patterns, 0);
		
		times[start] = 0;
		patterns[start] = 1;
		queue.add(new State(start, 0));
		
		while(!queue.isEmpty()){
			final State state = queue.poll();
			final int node = state.node;
			final long time = state.time;
			
			if(times[node] < state.time){ continue; }
			
			for(final Entry<Integer, Long> entry : adj.get(node).entrySet()){
				final int next = entry.getKey();
				final long next_time = time + entry.getValue();
				
				if(times[next] < next_time){ continue; }
				
				if(times[next] > next_time){
					times[next] = next_time;
					patterns[next] = patterns[node];
					
					queue.add(new State(next, next_time));
				}else{
					patterns[next] += patterns[node];
					patterns[next] %= MOD;
				}
			}
		}
	}
	
	public static void main(String[] args) throws IOException {
		Scanner sc = new Scanner(System.in);
		
		final int N = sc.nextInt();
		final int M = sc.nextInt();
		final int S = sc.nextInt() - 1;
		final int T = sc.nextInt() - 1;
		
		ArrayList<HashMap<Integer, Long>> adj = new ArrayList<HashMap<Integer, Long>>();
		ArrayList<HashSet<Integer>> bridges = new ArrayList<HashSet<Integer>>();
		for(int i = 0; i < N; i++){ adj.add(new HashMap<Integer, Long>()); }
		for(int i = 0; i < N; i++){ bridges.add(new HashSet<Integer>()); }
		
		for(int i = 0; i < M; i++){
			final int U = sc.nextInt() - 1;
			final int V = sc.nextInt() - 1;
			final long D = sc.nextLong();
			
			adj.get(U).put(V, D);
			adj.get(V).put(U, D);
		}
		
		int[] pres = new int[N]; Arrays.fill(pres, -1);
		int[] lows = new int[N]; Arrays.fill(lows, -1);
		dfs(pres, lows, 0, -1, adj, bridges);
		
		long[] from_S_times = new long[N];
		long[] from_S_patterns = new long[N];
		long[] from_T_times = new long[N];
		long[] from_T_patterns = new long[N];
		
		dijkstra(N, S, from_S_times, from_S_patterns, adj);
		dijkstra(N, T, from_T_times, from_T_patterns, adj);
		
		//System.out.println(from_S_patterns[T] + " " + from_T_patterns[S]);
		//System.out.println(Arrays.toString(from_S_times));
		
		long answer = (from_S_patterns[T] * from_T_patterns[S]) % MOD;
		//System.out.println(answer);
		
		// 同時に同じ頂点に到着する組み合わせは無視する
		//*
		for(int node = 0; node < N; node++){
			if(from_S_times[node] == from_T_times[node]){
				final long dup = (from_S_patterns[node] * from_T_patterns[node]) % MOD;
				answer = (MOD + answer - dup) % MOD;
			}
		}
		//*/
		
		// 辺でぶつかる組み合わせを除去する
		for(int node = 0; node < N; node++){
			for(final Entry<Integer, Long> entry : adj.get(node).entrySet()){
				final int next = entry.getKey();
				final long cost = entry.getValue();
				
				final long S_diff = from_S_times[next] - from_S_times[node];
				final long T_diff = from_T_times[node] - from_T_times[next];
				if(S_diff != cost || T_diff != cost){ continue; }
				
				if(from_S_times[next] <= from_T_times[next]){ continue; }
				if(from_S_times[node] >= from_T_times[node]){ continue; }
				
				if(bridges.get(node).contains(next)){
					System.out.println(0);
					return;
				}else{
					final long dup = (from_S_patterns[node] * from_T_patterns[next]) % MOD;
					//System.out.println(node + " " + next + " " + dup);
					answer = (MOD + answer - dup) % MOD;
				}
			}
		}
		
		/*
		System.out.println(from_S_used_edge);		
		System.out.println(from_S_times[T] + " : " + Arrays.toString(from_S_times));
		System.out.println(from_T_times[S] + " : " + Arrays.toString(from_T_times));
		*/
		System.out.println(answer);
	}
	
	public static class Scanner implements Closeable {
		private BufferedReader br;
		private StringTokenizer tok;
 
		public Scanner(InputStream is) throws IOException {
			br = new BufferedReader(new InputStreamReader(is));
		}	
 
		private void getLine() throws IOException {
			while (!hasNext()) {
				tok = new StringTokenizer(br.readLine());
			}
		}
 
		private boolean hasNext() {
			return tok != null && tok.hasMoreTokens();
		}
 
		public String next() throws IOException {
			getLine();
			return tok.nextToken();
		}
 
		public int nextInt() throws IOException {
			return Integer.parseInt(next());
		}
 
		public long nextLong() throws IOException {
			return Long.parseLong(next());
		}
 
		public double nextDouble() throws IOException {
			return Double.parseDouble(next());
		}
 
		public void close() throws IOException {
			br.close();
		}
	}
}