#include <bits/stdc++.h>
using namespace std;
class Input {
 public:
  bool isBlank(char c) {
    if ((c == '\r') || (c == '\n') || (c == ' ') || (c == '\t')) return true;
    return false;
  }
  bool isEOL(char c) {
    if ((c == '\r') || (c == '\n')) return true;
    return false;
  }
  bool isEmpty(string s) { return s.empty(); }
  void iStrip(string& s) {
    while (!s.empty() && isBlank(*s.begin())) s.erase(0, 1);
    while (!s.empty() && isBlank(*s.rbegin())) s.pop_back();
  }
  string strip(const string& s) {
    string _s(s);
    iStrip(_s);
    return _s;
  }
  string readline() {
    string s;
    char c;
    while (!((c = getchar()) == EOF) && (!isEOL(c))) s.push_back(c);
    return s;
  }
  string read() {
    string S;
    for (string s; getline(cin, s);) {
      if (*s.rbegin() == '\r') s.pop_back();
      S += s + '\n';
    }
    return S;
  }
  vector<string> split(string s, string delimiter = " ") {
    vector<string> output;
    output.push_back(s);
    while (true) {
      auto cur_str = *output.rbegin();
      output.pop_back();
      auto pos = cur_str.find(delimiter);
      if (pos < cur_str.size()) {
        output.push_back(cur_str.substr(0, pos));
        output.push_back(cur_str.substr(pos + delimiter.size()));
      } else {
        output.push_back(cur_str);
        break;
      }
    }
    return move(output);
  }
  vector<string> purgeSplit(string s, string delimiter = " ") {
    auto splitString = split(s, delimiter);
    vector<string> output;
    for (auto x : splitString)
      if (!isEmpty(x)) output.push_back(x);
    return output;
  }
  string join(vector<string> to_join, string delimiter = " ") {
    int n = to_join.size();
    ostringstream os;
    for (int i = 0; i < n; i++) {
      if (i == n - 1)
        os << to_join[i];
      else
        os << to_join[i] << delimiter;
    }
    return os.str();
  }
  template <typename T>
  vector<T> spread(vector<string> to_map) {
    vector<T> output;
    istringstream is(join(to_map));
    T cur;
    while (is >> cur) output.push_back(cur);
    return output;
  }
  template <typename T>
  T readval() {
    T x;
    string s = readline();
    istringstream is(s);
    is >> x;
    return x;
  }
} In;
int topSort[(int)1e5 + 10];
int tcount = (int)1e5 + 10;
vector<int> connections[(int)1e5 + 10];
bool recStack[(int)1e5 + 10];
unordered_set<int> seen;
bool dfs(int i) {
  if (recStack[i]) return false;
  if (seen.find(i) != seen.end()) return true;
  seen.insert(i);
  recStack[i] = true;
  bool pos = true;
  for (__typeof((connections[i]).begin()) it = (connections[i]).begin();
       it != (connections[i]).end(); it++)
    pos &= dfs(*it);
  recStack[i] = false;
  return pos;
}
void generateTopSort(int i) {
  if (topSort[i] != -1) return;
  for (__typeof((connections[i]).begin()) it = (connections[i]).begin();
       it != (connections[i]).end(); it++)
    generateTopSort(*it);
  topSort[i] = tcount;
  tcount--;
}
int main() {
  int n, m;
  cin >> n >> m;
  vector<tuple<int, int, int>> roads;
  for (__typeof(m) i = (0) - ((0) > (m)); i != (m) - ((0) > (m));
       i += 1 - 2 * ((0) > (m))) {
    int u, v, c;
    cin >> u >> v >> c;
    roads.emplace_back(make_tuple(u, v, c));
  }
  int l = -1, r = 1e9;
  while ((r - l) > 1) {
    for (__typeof(n + 1) i = (1) - ((1) > (n + 1));
         i != (n + 1) - ((1) > (n + 1)); i += 1 - 2 * ((1) > (n + 1)))
      connections[i].clear();
    seen.clear();
    memset(recStack, false, sizeof recStack);
    int m = (r + l) >> 1;
    for (__typeof((roads).begin()) it = (roads).begin(); it != (roads).end();
         it++) {
      int u, v, c;
      tie(u, v, c) = *it;
      if (c > m) connections[u].emplace_back(v);
    }
    bool pos = true;
    for (__typeof(n + 1) i = (1) - ((1) > (n + 1));
         i != (n + 1) - ((1) > (n + 1)); i += 1 - 2 * ((1) > (n + 1))) {
      if (seen.find(i) == seen.end()) {
        pos &= dfs(i);
      }
    }
    if (pos)
      r = m;
    else
      l = m;
  }
  for (__typeof(n + 1) i = (1) - ((1) > (n + 1));
       i != (n + 1) - ((1) > (n + 1)); i += 1 - 2 * ((1) > (n + 1)))
    connections[i].clear();
  for (__typeof((roads).begin()) it = (roads).begin(); it != (roads).end();
       it++) {
    int u, v, c;
    tie(u, v, c) = *it;
    if (c > r) connections[u].emplace_back(v);
  }
  memset(topSort, -1, sizeof topSort);
  for (__typeof(n + 1) i = (1) - ((1) > (n + 1));
       i != (n + 1) - ((1) > (n + 1)); i += 1 - 2 * ((1) > (n + 1))) {
    if (topSort[i] == -1) generateTopSort(i);
  }
  vector<int> changeableEdges;
  for (__typeof(m) i = (0) - ((0) > (m)); i != (m) - ((0) > (m));
       i += 1 - 2 * ((0) > (m))) {
    int u, v, c;
    tie(u, v, c) = roads[i];
    if (topSort[v] < topSort[u]) changeableEdges.emplace_back(i + 1);
  }
  cout << r << " " << ((int)((changeableEdges).size())) << endl;
  for (__typeof((changeableEdges).begin()) it = (changeableEdges).begin();
       it != (changeableEdges).end(); it++)
    cout << *it << " ";
  cout << endl;
}
