#include <bits/stdc++.h>
using namespace std;

static int W, G, P, N;
static uint64_t fullMask;
static vector<uint64_t> usedPair;
static vector<vector<vector<int>>> weeks;
static vector<uint64_t> rems;
static chrono::steady_clock::time_point deadline;
static mt19937_64 rng;
static long long nodes = 0;

static vector<int> bits(uint64_t m) {
    vector<int> r;
    while (m) {
        int b = __builtin_ctzll(m);
        r.push_back(b);
        m &= m - 1;
    }
    return r;
}

static bool time_ok() {
    return chrono::steady_clock::now() < deadline;
}

static bool rec_triples(int week) {
    if ((++nodes & 0x3ffff) == 0 && !time_ok()) return false;
    if (week == W) return true;
    uint64_t rem = rems[week];
    if (!rem) return rec_triples(week + 1);
    auto players = bits(rem);
    int a = -1, best = 1000000;
    shuffle(players.begin(), players.end(), rng);
    for (int x : players) {
        int deg = __builtin_popcountll((~usedPair[x]) & rem);
        if (deg < best) best = deg, a = x;
    }
    vector<int> cand;
    for (int x : players) if (x != a && !((usedPair[a] >> x) & 1ULL)) cand.push_back(x);
    struct Choice { int score, b, c; uint64_t rnd; };
    vector<Choice> choices;
    for (int i = 0; i < (int)cand.size(); ++i) {
        for (int j = i + 1; j < (int)cand.size(); ++j) {
            int b = cand[i], c = cand[j];
            if ((usedPair[b] >> c) & 1ULL) continue;
            int sc = __builtin_popcountll((~usedPair[a]) & rem)
                   + __builtin_popcountll((~usedPair[b]) & rem)
                   + __builtin_popcountll((~usedPair[c]) & rem);
            choices.push_back({sc, b, c, rng()});
        }
    }
    sort(choices.begin(), choices.end(), [](const Choice& x, const Choice& y) {
        if (x.score != y.score) return x.score < y.score;
        return x.rnd < y.rnd;
    });
    int lim = min<int>(choices.size(), 140);
    for (int idx = 0; idx < lim; ++idx) {
        auto ch = choices[idx];
        int b = ch.b, c = ch.c;
        uint64_t mask = (1ULL << a) | (1ULL << b) | (1ULL << c);
        weeks[week].push_back({a,b,c});
        rems[week] = rem & ~mask;
        usedPair[a] |= (1ULL << b) | (1ULL << c);
        usedPair[b] |= (1ULL << a) | (1ULL << c);
        usedPair[c] |= (1ULL << a) | (1ULL << b);
        if (rec_triples(week)) return true;
        usedPair[a] &= ~((1ULL << b) | (1ULL << c));
        usedPair[b] &= ~((1ULL << a) | (1ULL << c));
        usedPair[c] &= ~((1ULL << a) | (1ULL << b));
        rems[week] = rem;
        weeks[week].pop_back();
    }
    return false;
}

static bool add_group(int week, const vector<int>& grp) {
    weeks[week].push_back(grp);
    for (int i = 0; i < (int)grp.size(); ++i) {
        for (int j = i + 1; j < (int)grp.size(); ++j) {
            int a = grp[i], b = grp[j];
            if ((usedPair[a] >> b) & 1ULL) return false;
            usedPair[a] |= 1ULL << b;
            usedPair[b] |= 1ULL << a;
        }
    }
    return true;
}

int main(int argc, char** argv) {
    if (argc < 5) {
        cerr << "usage: sgp_gen W G P seconds [seed]\n";
        return 2;
    }
    W = atoi(argv[1]); G = atoi(argv[2]); P = atoi(argv[3]); int sec = atoi(argv[4]);
    uint64_t seed = argc > 5 ? strtoull(argv[5], nullptr, 10) : 1;
    rng.seed(seed);
    N = G * P;
    if (N > 60) return 3;
    fullMask = (N == 64 ? ~0ULL : ((1ULL << N) - 1));
    deadline = chrono::steady_clock::now() + chrono::seconds(sec);
    if (P != 3) return 4;
    while (time_ok()) {
        nodes = 0;
        usedPair.assign(N, 0);
        weeks.assign(W, {});
        rems.assign(W, fullMask);
        rems[0] = 0;
        bool ok = true;
        for (int gr = 0; gr < G && ok; ++gr) ok = add_group(0, {3*gr, 3*gr+1, 3*gr+2});
        int start = 1;
        if (W > 1) {
            rems[1] = 0;
            start = 2;
            vector<int> perm(G);
            iota(perm.begin(), perm.end(), 0);
            shuffle(perm.begin(), perm.end(), rng);
            for (int gr = 0; gr < G && ok; ++gr) {
                int c = perm[gr];
                ok = add_group(1, {c, G + ((c + gr) % G), 2*G + ((c + 2*gr) % G)});
            }
        }
        if (ok && rec_triples(start)) {
            cout << "solution nodes=" << nodes << "\n";
            for (int w = 0; w < W; ++w) {
                cout << "week " << w << "\n";
                for (auto &grp : weeks[w]) {
                    for (int i = 0; i < (int)grp.size(); ++i) {
                        if (i) cout << ' ';
                        cout << grp[i];
                    }
                    cout << "\n";
                }
            }
            return 0;
        }
    }
    cerr << "no solution nodes=" << nodes << "\n";
    return 1;
}
