#include <stdio.h>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;

#define INF 1000000100
#define pb push_back

struct HeapEntry {
   int id, d, pr;
};

struct cmpHEntry {
   bool operator () (const HeapEntry & h1, const HeapEntry & h2) const {
      if(h1.d == h2.d) if(h1.id == h2.id) return h1.pr < h2.pr; return h1.id < h2.id; return h1.d < h2.d;
   }
};

struct Edge {
   int node, dist;
};

int n, m, A, B;
set<HeapEntry, cmpHEntry> heap;
vector<Edge> * edge;

bool cmpEdge (Edge e1, Edge e2) {
   if(e1.node == e2.node) return e1.dist < e2.dist;
   return e1.node < e2.node;
}

void InitEdge (int node) {
   if(edge[node].size() == 0) return;

   vector<Edge> tmp;
   sort(edge[node].begin(), edge[node].end(), cmpEdge);

   tmp.pb(edge[node][0]);
   for(int i = 1; i < edge[node].size(); i++) if(edge[node][i].node != edge[node][i-1].node) tmp.pb(edge[node][i]);

   edge[node].clear();
   for(int i = 0 ; i< tmp.size(); i++) edge[node].pb(tmp[i]);
}

void Input() {
   FILE *fin = fopen("nishtel.in", "r");
   fscanf(fin, "%d%d%d%d", &n, &m, &A, &B);

   edge = new vector<Edge>[n+1];

   for(int i = 0; i < m; i++) {
      Edge newEdge;
      int x, y, w;
      fscanf(fin, "%d%d%d", &x, &y, &w);

      newEdge.node = y;
      newEdge.dist = w;

      edge[x].pb(newEdge);
   }

   for(int i = 1; i <= n; i++) InitEdge(i);
}

void Dijkstra (int * &sol, int node) {
   sol = new int[n+1];
   for(int i = 1; i <= n; i++) sol[i] = INF;

   heap.clear();

   HeapEntry he, newhe;
   he.id = node;
   he.pr = -1;
   he.d = 0;

   heap.insert(he);

   while(heap.size() > 0) {
      he = *heap.begin();
      heap.erase(heap.begin());

      if(sol[he.id] > he.d) {
         sol[he.id] = he.d;

         int curr_node = he.id, curr_dist = he.d;
         newhe.pr = curr_node;

         for(int i = 0; i < edge[curr_node].size(); i++) {
            newhe.id = edge[curr_node][i].node;
            newhe.d = curr_dist + edge[curr_node][i].dist;
            heap.insert(newhe);
         }
      }
   }
}

int *dA, *dB;
bool * common;

int Solve() {
   Dijkstra(dA, A);
   Dijkstra(dB, B);

   int sol = 2 * (n-1);
   common = new bool[n+1];
   for(int i = 1; i <= n; i++) common[i] = false;

   for(int i = 1; i <= n; i++) {
      printf("%d %d\n", dA[i], dB[i]);
   }

   printf("!\n");

   for(int node = 1; node <= n; node++) {
      int next, w;

      printf("%d\n", node);

      for(int i = 0; i < edge[node].size(); i++) {
         next = edge[node][i].node;
         w = edge[node][i].dist;

         printf(" %d %d\n", next, w);

         if(dA[next] - dA[node] == w && dB[next] - dB[node] == w && !common[next]) { sol--; common[next] = true; }
      }
   }


   printf("!\n");

   return m - sol;
}

void Output() {
   FILE * fout = fopen("nishtel.out", "w");
   fprintf(fout, "%d\n", Solve());
}

int main() {
   Input();
   Output();
}
