#include <bits/stdc++.h>

using namespace std;

#define CHECK(x) if(!(x)) { perror(#x); exit(1); }

[[noreturn]] void grade(float score, const char *text) {
  printf("%f\n", score);
  fprintf(stderr, "%s\n", text);
  exit(0);
}
void judge_message(string msg) {
  cerr <<  msg << endl;
}
[[noreturn]] void wrong_answer(string msg) {
  judge_message(msg);
  grade(0, "translate:wrong");
}
[[noreturn]] void accept_with_score(float score) {
  judge_message("score: " + to_string(score));
  // TODO: "translate:success" if full score
  grade(score, "translate:partial");
}
[[noreturn]] void judge_error(string msg) {
  judge_message(msg);
  exit(1);
}

const int NUM_PROC = 100;
const int64_t MAX_POS = 1e18;
const int MAX_ROUNDS = 500;

struct Process {
  FILE *fifo_in, *fifo_out;
  int uid = 0;
  Process(char *in, char *out) {
    fifo_out = fopen(in, "w");
    fifo_in = fopen(out, "r");
    CHECK(fifo_in);
    CHECK(fifo_out);
  }
  void write(string line) {
    int rc = fputs((line+"\n").c_str(), fifo_out);
    if(rc == EOF)
      wrong_answer("failed to write, exited too early?");
    rc = fflush(fifo_out);
    if(rc != 0)
      wrong_answer("broken pipe, exited too early?");
  }
  struct Query {
    enum { UNKNOWN, READ, WRITE, ANSWER } type = UNKNOWN;
    int64_t N = 0;
    int64_t P = 0;
    int64_t V = 0;
  };
  Query read() {
    Query q;
    char cmd = '@';
    long long a = -1, b = -1;
    int r = -1;

    do {
      cmd = fgetc(fifo_in);
      if (cmd == EOF) wrong_answer("unexpected eof");
    } while (isspace(cmd));


    if (cmd == '!') {
      r = fscanf(fifo_in, " %lld", &a);
      if (r != 1) wrong_answer("failed to parse ! int");
      int c;
      do {
        c = fgetc(fifo_in);
      } while (isspace(c));
      if (c != EOF) wrong_answer("trailing garbage \"" + string{char(c)} + "\"");
      if (a < 0) wrong_answer("a < 0 in answer");
      q.type = Query::ANSWER;
      q.N = a;
      return q;
    }
    else if (cmd == 'w') {
      r = fscanf(fifo_in, " %lld %lld", &a, &b);
      if (r != 2) wrong_answer("failed to parse w int int");
      if(a < 0) wrong_answer("a < 0 in write");

      q.type = Query::WRITE;
      q.P = a;
      q.V = b;
      return q;
    }
    else if (cmd == 'r') {
      r = fscanf(fifo_in, " %lld", &a);
      if (r != 1) wrong_answer("failed to parse w int int");
      if(a < 0) wrong_answer("a < 0 in read");

      q.type = Query::READ;
      q.P = a;
      return q;
    }

    wrong_answer("unexpected command " + cmd);
  }
  ~Process(){
    fclose(fifo_in);
    fclose(fifo_out);
  }
};

struct Testcase {
  int n, m;
  vector<unique_ptr<Process>> proc;
  map<int64_t,int> locations;
  int remain;
  int id = 0;
};


int main(int argc, char **argv) {
  signal(SIGPIPE, SIG_IGN);

  int pr = 0;
  array<unique_ptr<Process>,NUM_PROC> p;
  for(int i = 0; i < NUM_PROC; ++i) {
    p[i] = make_unique<Process>(argv[2*i+2], argv[2*i+1]);
  }

  int number_tests, m;
  cin >> number_tests >> m;
  vector<Testcase> tests(number_tests);
  int tcs = 0;
  for(auto &t : tests) {
    cin >> t.n;
    t.m = m;
    t.id = ++tcs;
    t.remain = t.n;
    t.proc.resize(t.n);
    for(int i = 0; i < t.n; ++i) {
      int uid;
      cin>>uid;
      t.proc[i] = std::move(p[pr++]);
      string s = to_string(uid) + " " + to_string(m);
      t.proc[i]->uid = uid;
      t.proc[i]->write(s);
    }
  }


  while(pr < 100) { // add a bunch of n=1
    tests.emplace_back();
    auto &t = tests.back();
    t.m = m, t.n = t.remain = 1;
    t.proc.emplace_back(std::move(p[pr++]));
    string s = to_string(0) + " " + to_string(m);
    t.proc[0]->uid = 0;
    t.proc[0]->write(s);
  }



  auto any_ongoing = [&](){
    for(auto &t : tests)
      if(t.remain) return true;
    return false;
  };

  int rounds = 0;
  int max_write = 0;
  while(any_ongoing()) {
    rounds++;
    if(rounds > MAX_ROUNDS) wrong_answer("too many rounds");
    map<int64_t,int> writes_any_test_case_to_id;

    for(auto &t : tests) if(t.remain) {
      map<int64_t,int> writes;
      for(unsigned i = 0; i < t.proc.size(); ++i) if(t.proc[i]) {
        auto q = t.proc[i]->read();
        if(q.type == Process::Query::ANSWER) {
          if(q.N != t.n)
            wrong_answer("guess " + to_string(q.N) + "; expected" + to_string(t.n));
          // proc is done, close pipes
          t.proc[i].reset();
          --t.remain;
        }
        else {
          if(q.P < 0 || q.P >= MAX_POS)
            wrong_answer("read outside of array");

          if(q.type == Process::Query::READ) {
            t.proc[i]->write(to_string(t.locations[q.P]));
          }
          else if(q.type == Process::Query::WRITE) {
            if(q.V < 0 || q.V >= int(1e9))
              wrong_answer("write outside range");
            if(writes.count(q.P))
              wrong_answer("double write");
            writes[q.P] = q.V;
            if(rounds <= 2) {
              // in first two rounds any write can only safely be based on ID,
              // so we can guard against double writes across testcases.
              if(writes_any_test_case_to_id.count(q.P)
                 && writes_any_test_case_to_id[q.P] != t.proc[i]->uid)
                wrong_answer("potential double write");
              writes_any_test_case_to_id[q.P] = t.proc[i]->uid;
            }
            max_write = max<int>(q.V, max_write);
          }
          else judge_error("bad query type; impossible?");
        }
      }
      for(auto [a,b] : writes) t.locations[a] = b;
    }
  }

  judge_message("used: " + to_string(rounds));
  // TODO: scoring function
  float score = 0.01 * min(100.0, (14.0 + pow(rounds-555.0, 4.0)/666666666.0));
  if(max_write > 1) score /= 4;
  accept_with_score(score);
}
