#include using namespace std; struct FastScanner { static const int BUFSIZE = 1 << 20; int idx = 0, sz = 0; char buf[BUFSIZE]; inline char gc() { if (idx >= sz) { sz = (int)fread(buf, 1, BUFSIZE, stdin); idx = 0; if (!sz) return 0; } return buf[idx++]; } template bool readInt(T &out) { char c; T sign = 1; out = 0; do { c = gc(); if (!c) return false; } while (c <= ' '); if (c == '-') { sign = -1; c = gc(); } for (; c >= '0' && c <= '9'; c = gc()) out = out * 10 + (c - '0'); out *= sign; return true; } } fs; static uint64_t rngState; static inline uint64_t splitmix64() { uint64_t x = (rngState += 0x9e3779b97f4a7c15ULL); x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; return x ^ (x >> 31); } static inline int randInt(int n) { return (int)(splitmix64() % n); } chrono::high_resolution_clock::time_point t0; inline double elapsed() { return chrono::duration(chrono::high_resolution_clock::now() - t0).count(); } int n, m; int *adjData, *radjData; int *adjStart, *radjStart; int *degOut, *degIn; int *adjSorted; inline bool hasEdge(int u, int v) { return binary_search(adjSorted + adjStart[u], adjSorted + adjStart[u] + degOut[u], v); } vector bestPath; void improvePath(vector& path, double timeLim) { if (path.empty() || (int)path.size() == n) return; int *nxt = (int*)calloc(n + 1, sizeof(int)); int *prv = (int*)calloc(n + 1, sizeof(int)); char *used = (char*)calloc(n + 1, sizeof(char)); for (int v : path) used[v] = 1; for (int i = 0; i + 1 < (int)path.size(); i++) { nxt[path[i]] = path[i + 1]; prv[path[i + 1]] = path[i]; } int head = path[0], tail = path.back(); auto extendEnds = [&]() { while (true) { bool prog = false; while (true) { int best = 0; int s = adjStart[tail], l = degOut[tail]; for (int j = 0; j < l; j++) if (!used[adjData[s + j]]) { best = adjData[s + j]; break; } if (!best) break; nxt[tail] = best; prv[best] = tail; used[best] = 1; tail = best; prog = true; } while (true) { int best = 0; int s = radjStart[head], l = degIn[head]; for (int j = 0; j < l; j++) if (!used[radjData[s + j]]) { best = radjData[s + j]; break; } if (!best) break; prv[head] = best; nxt[best] = head; used[best] = 1; head = best; prog = true; } if (!prog) break; } }; auto insertionPass = [&]() -> bool { bool changed = false; int u = head; while (u) { int w = nxt[u]; if (!w) break; int s = adjStart[u], l = degOut[u]; for (int j = 0; j < l; j++) { int v = adjData[s + j]; if (!used[v] && hasEdge(v, w)) { nxt[u] = v; prv[v] = u; nxt[v] = w; prv[w] = v; used[v] = 1; changed = true; w = v; } } u = w; } return changed; }; auto reverseInsertion = [&]() -> bool { bool changed = false; for (int v = 1; v <= n; v++) { if (used[v]) continue; bool ins = false; int s = adjStart[v], l = degOut[v]; for (int j = 0; j < l && !ins; j++) { int w = adjData[s + j]; if (!used[w]) continue; int u2 = prv[w]; if (u2 && hasEdge(u2, v)) { nxt[u2] = v; prv[v] = u2; nxt[v] = w; prv[w] = v; used[v] = 1; changed = true; ins = true; } } if (ins) continue; int rs = radjStart[v], rl = degIn[v]; for (int j = 0; j < rl && !ins; j++) { int u2 = radjData[rs + j]; if (!used[u2]) continue; int w = nxt[u2]; if (w && hasEdge(v, w)) { nxt[u2] = v; prv[v] = u2; nxt[v] = w; prv[w] = v; used[v] = 1; changed = true; ins = true; } } if (ins) continue; if (hasEdge(v, head)) { prv[head] = v; nxt[v] = head; head = v; used[v] = 1; changed = true; } else if (hasEdge(tail, v)) { nxt[tail] = v; prv[v] = tail; tail = v; used[v] = 1; changed = true; } } return changed; }; extendEnds(); for (int pass = 0; pass < 30; pass++) { bool ch = insertionPass(); extendEnds(); if (!ch) break; } for (int pass = 0; pass < 30 && elapsed() < timeLim; pass++) { bool ch = reverseInsertion(); extendEnds(); insertionPass(); extendEnds(); if (!ch) break; } // 2-vertex chain insertion if (n <= 1000 && elapsed() < timeLim) { for (int pass = 0; pass < 5 && elapsed() < timeLim; pass++) { bool changed = false; for (int v1 = 1; v1 <= n && elapsed() < timeLim; v1++) { if (used[v1]) continue; bool done = false; int s1 = adjStart[v1], l1 = degOut[v1]; for (int j1 = 0; j1 < l1 && !done; j1++) { int v2 = adjData[s1 + j1]; if (used[v2] || v2 == v1) continue; int rs1 = radjStart[v1], rl1 = degIn[v1]; for (int j2 = 0; j2 < rl1 && !done; j2++) { int u = radjData[rs1 + j2]; if (!used[u]) continue; int w = nxt[u]; if (w && hasEdge(v2, w)) { nxt[u] = v1; prv[v1] = u; nxt[v1] = v2; prv[v2] = v1; nxt[v2] = w; prv[w] = v2; used[v1] = 1; used[v2] = 1; changed = true; done = true; } } // Also try: v1->v2 at head/tail if (!done && hasEdge(v2, head)) { int rs2 = radjStart[v1], rl2 = degIn[v1]; // v1 needs an in-edge from somewhere... actually just check if v1 can be at head // Nothing feeds into v1 from the path, but v1 is the new head // We need: v1 -> v2 -> head nxt[v1] = v2; prv[v2] = v1; nxt[v2] = head; prv[head] = v2; head = v1; used[v1] = 1; used[v2] = 1; changed = true; done = true; } if (!done && hasEdge(tail, v1)) { nxt[tail] = v1; prv[v1] = tail; nxt[v1] = v2; prv[v2] = v1; tail = v2; used[v1] = 1; used[v2] = 1; changed = true; done = true; } } } extendEnds(); insertionPass(); reverseInsertion(); extendEnds(); if (!changed) break; } } // Reroute: for each edge u->w in path, try u->v->w where v is unused if (n <= 1000 && elapsed() < timeLim) { for (int pass = 0; pass < 5 && elapsed() < timeLim; pass++) { bool changed = false; int u = head; while (u && elapsed() < timeLim) { int w = nxt[u]; if (!w) break; int s = adjStart[u], l = degOut[u]; bool rerouted = false; for (int j = 0; j < l && !rerouted; j++) { int v = adjData[s + j]; if (v == w || used[v]) continue; if (hasEdge(v, w)) { nxt[u] = v; prv[v] = u; nxt[v] = w; prv[w] = v; used[v] = 1; changed = true; rerouted = true; } } if (!rerouted) { // Try u -> v1 -> v2 -> w for (int j1 = 0; j1 < l && !rerouted; j1++) { int v1 = adjData[s + j1]; if (v1 == w || used[v1]) continue; int s2 = adjStart[v1], l2 = degOut[v1]; for (int j2 = 0; j2 < l2 && !rerouted; j2++) { int v2 = adjData[s2 + j2]; if (v2 == w || v2 == v1 || used[v2]) continue; if (hasEdge(v2, w)) { nxt[u] = v1; prv[v1] = u; nxt[v1] = v2; prv[v2] = v1; nxt[v2] = w; prv[w] = v2; used[v1] = 1; used[v2] = 1; changed = true; rerouted = true; } } } } u = nxt[u]; } extendEnds(); insertionPass(); reverseInsertion(); extendEnds(); if (!changed) break; } } // Vertex swap: for each unused v, try to replace a path vertex b with v // such that b can be reinserted elsewhere if (n <= 500 && elapsed() < timeLim) { for (int pass = 0; pass < 3 && elapsed() < timeLim; pass++) { bool changed = false; for (int v = 1; v <= n && elapsed() < timeLim; v++) { if (used[v]) continue; // For each path node b, try replacing b with v // Requirements: prv[b]->v and v->nxt[b] (v replaces b) // Then b is free and can be reinserted for (int b = 1; b <= n; b++) { if (!used[b]) continue; int pb = prv[b], nb = nxt[b]; if (!pb && !nb) continue; // b is alone bool canReplace = false; if (pb && nb) canReplace = hasEdge(pb, v) && hasEdge(v, nb); else if (!pb && nb) canReplace = hasEdge(v, nb); // b is head else if (pb && !nb) canReplace = hasEdge(pb, v); // b is tail if (!canReplace) continue; // Remove b, insert v in b's place if (pb) nxt[pb] = v; else head = v; if (nb) prv[nb] = v; else tail = v; prv[v] = pb; nxt[v] = nb; used[v] = 1; used[b] = 0; prv[b] = 0; nxt[b] = 0; // Try to reinsert b bool reins = false; // Try between path nodes for (int j = 0; j < degOut[b] && !reins; j++) { int w = adjData[adjStart[b] + j]; if (!used[w]) continue; int pu = prv[w]; if (pu && hasEdge(pu, b)) { nxt[pu] = b; prv[b] = pu; nxt[b] = w; prv[w] = b; used[b] = 1; reins = true; } } if (!reins) { for (int j = 0; j < degIn[b] && !reins; j++) { int u2 = radjData[radjStart[b] + j]; if (!used[u2]) continue; int w = nxt[u2]; if (w && hasEdge(b, w)) { nxt[u2] = b; prv[b] = u2; nxt[b] = w; prv[w] = b; used[b] = 1; reins = true; } } } if (!reins && hasEdge(b, head)) { prv[head] = b; nxt[b] = head; head = b; used[b] = 1; reins = true; } if (!reins && hasEdge(tail, b)) { nxt[tail] = b; prv[b] = tail; tail = b; used[b] = 1; reins = true; } if (reins) { changed = true; break; // done with v } else { // Undo: put b back, remove v if (prv[v]) nxt[prv[v]] = b; else head = b; if (nxt[v]) prv[nxt[v]] = b; else tail = b; prv[b] = prv[v]; nxt[b] = nxt[v]; used[b] = 1; used[v] = 0; prv[v] = 0; nxt[v] = 0; } } } if (changed) { extendEnds(); insertionPass(); reverseInsertion(); extendEnds(); } else break; } } path.clear(); for (int u = head; u; u = nxt[u]) { path.push_back(u); if ((int)path.size() > n) break; } free(nxt); free(prv); free(used); } vector greedyPath(int start, int mode = 0) { // mode 0: first available, mode 1: Warnsdorff, mode 2: random char *used = (char*)calloc(n + 1, sizeof(char)); deque path; path.push_back(start); used[start] = 1; while (true) { bool prog = false; while (true) { int t = path.back(); int best = 0; int s = adjStart[t], l = degOut[t]; if (mode == 1) { int bestScore = INT_MAX; for (int j = 0; j < l; j++) { int v = adjData[s + j]; if (!used[v]) { int cnt = 0; int vs = adjStart[v], vl = degOut[v]; for (int k = 0; k < vl; k++) if (!used[adjData[vs + k]]) cnt++; if (cnt < bestScore) { bestScore = cnt; best = v; } } } } else if (mode == 2) { // Random int cnt = 0; for (int j = 0; j < l; j++) if (!used[adjData[s + j]]) cnt++; if (cnt > 0) { int pick = randInt(cnt), k = 0; for (int j = 0; j < l; j++) if (!used[adjData[s + j]]) { if (k == pick) { best = adjData[s + j]; break; } k++; } } } else { for (int j = 0; j < l; j++) if (!used[adjData[s + j]]) { best = adjData[s + j]; break; } } if (!best) break; path.push_back(best); used[best] = 1; prog = true; } while (true) { int h = path.front(); int best = 0; int s = radjStart[h], l = degIn[h]; if (mode == 1) { int bestScore = INT_MAX; for (int j = 0; j < l; j++) { int u = radjData[s + j]; if (!used[u]) { int cnt = 0; int us = radjStart[u], ul = degIn[u]; for (int k = 0; k < ul; k++) if (!used[radjData[us + k]]) cnt++; if (cnt < bestScore) { bestScore = cnt; best = u; } } } } else if (mode == 2) { int cnt = 0; for (int j = 0; j < l; j++) if (!used[radjData[s + j]]) cnt++; if (cnt > 0) { int pick = randInt(cnt), k = 0; for (int j = 0; j < l; j++) if (!used[radjData[s + j]]) { if (k == pick) { best = radjData[s + j]; break; } k++; } } } else { for (int j = 0; j < l; j++) if (!used[radjData[s + j]]) { best = radjData[s + j]; break; } } if (!best) break; path.push_front(best); used[best] = 1; prog = true; } if (!prog) break; } vector result(path.begin(), path.end()); free(used); return result; } bool dfsBacktrackFrom(int start, double timeLim, bool randomize = false) { char *used = (char*)calloc(n + 1, sizeof(char)); int *pathArr = (int*)malloc(n * sizeof(int)); int maxDeg = 0; for (int u = 1; u <= n; u++) if (degOut[u] > maxDeg) maxDeg = degOut[u]; int *nbrBuf = (int*)malloc((long long)n * (maxDeg + 1) * sizeof(int)); int *nbrCnt = (int*)malloc(n * sizeof(int)); int *nbrIdx = (int*)malloc(n * sizeof(int)); pair *tmp = (pair*)malloc((maxDeg + 1) * sizeof(pair)); bool found = false; int depth = 0; pathArr[0] = start; used[start] = 1; auto computeNbrs = [&](int d) { int v = pathArr[d]; int s = adjStart[v], l = degOut[v]; int tc = 0; for (int j = 0; j < l; j++) { int w = adjData[s + j]; if (!used[w]) { int sc = 0; int ws = adjStart[w], wl = degOut[w]; for (int k = 0; k < wl; k++) if (!used[adjData[ws + k]]) sc++; if (randomize) sc = sc * 1000 + (int)(splitmix64() % 1000); tmp[tc++] = {sc, w}; } } sort(tmp, tmp + tc); long long base = (long long)d * (maxDeg + 1); for (int i = 0; i < tc; i++) nbrBuf[base + i] = tmp[i].second; nbrCnt[d] = tc; nbrIdx[d] = 0; }; computeNbrs(0); long long checks = 0; while (depth >= 0) { if (++checks % 100000 == 0 && elapsed() > timeLim) break; if (depth == n - 1) { bestPath.assign(pathArr, pathArr + n); found = true; break; } if (depth + 1 > (int)bestPath.size()) { bestPath.assign(pathArr, pathArr + depth + 1); } bool went_deeper = false; long long base = (long long)depth * (maxDeg + 1); while (nbrIdx[depth] < nbrCnt[depth]) { int w = nbrBuf[base + nbrIdx[depth]]; nbrIdx[depth]++; if (used[w]) continue; depth++; pathArr[depth] = w; used[w] = 1; computeNbrs(depth); went_deeper = true; break; } if (!went_deeper) { used[pathArr[depth]] = 0; depth--; } } free(used); free(pathArr); free(nbrBuf); free(nbrCnt); free(nbrIdx); free(tmp); return found; } vector dpOnOrder(const vector& order) { vector pos(n + 1), dp(n + 1, 1), par(n + 1, 0); for (int i = 0; i < n; i++) pos[order[i]] = i; for (int i = 0; i < n; i++) { int v = order[i]; int s = adjStart[v], l = degOut[v]; for (int j = 0; j < l; j++) { int w = adjData[s + j]; if (pos[w] > i && dp[v] + 1 > dp[w]) { dp[w] = dp[v] + 1; par[w] = v; } } } int bestV = 1, bestLen = dp[1]; for (int v = 2; v <= n; v++) if (dp[v] > bestLen) { bestLen = dp[v]; bestV = v; } vector path; int cur = bestV; while (cur) { path.push_back(cur); cur = par[cur]; } reverse(path.begin(), path.end()); return path; } int main() { rngState = chrono::high_resolution_clock::now().time_since_epoch().count(); t0 = chrono::high_resolution_clock::now(); fs.readInt(n); fs.readInt(m); for (int i = 0; i < 10; i++) { int x; fs.readInt(x); } adjStart = (int*)calloc(n + 1, sizeof(int)); radjStart = (int*)calloc(n + 1, sizeof(int)); degOut = (int*)calloc(n + 1, sizeof(int)); degIn = (int*)calloc(n + 1, sizeof(int)); int *edgeU = (int*)malloc(m * sizeof(int)); int *edgeV = (int*)malloc(m * sizeof(int)); for (int i = 0; i < m; i++) { fs.readInt(edgeU[i]); fs.readInt(edgeV[i]); degOut[edgeU[i]]++; degIn[edgeV[i]]++; } for (int u = 1; u <= n; u++) { adjStart[u] = (u == 1) ? 0 : adjStart[u - 1] + degOut[u - 1]; radjStart[u] = (u == 1) ? 0 : radjStart[u - 1] + degIn[u - 1]; } adjData = (int*)malloc(m * sizeof(int)); radjData = (int*)malloc(m * sizeof(int)); { int *ac = (int*)malloc((n + 1) * sizeof(int)); int *rc = (int*)malloc((n + 1) * sizeof(int)); for (int u = 1; u <= n; u++) { ac[u] = adjStart[u]; rc[u] = radjStart[u]; } for (int i = 0; i < m; i++) { adjData[ac[edgeU[i]]++] = edgeV[i]; radjData[rc[edgeV[i]]++] = edgeU[i]; } free(ac); free(rc); } free(edgeU); free(edgeV); adjSorted = (int*)malloc(m * sizeof(int)); memcpy(adjSorted, adjData, m * sizeof(int)); for (int u = 1; u <= n; u++) { sort(adjSorted + adjStart[u], adjSorted + adjStart[u] + degOut[u]); } if (n <= 200) { // Small graph: DFS + many random greedy + improve // Phase 1: Deterministic DFS from best starts { vector> starts; for (int u = 1; u <= n; u++) starts.push_back({-(degOut[u] - degIn[u]), u}); sort(starts.begin(), starts.end()); bool found = false; for (int i = 0; i < (int)starts.size() && elapsed() < 0.8 && !found; i++) { double rem = 0.8 - elapsed(); double t = max(rem / ((int)starts.size() - i), 0.01); found = dfsBacktrackFrom(starts[i].second, elapsed() + t, false); } // Phase 1b: Randomized DFS - many restarts if (!found) { while (elapsed() < 2.0 && (int)bestPath.size() < n) { int sv = randInt(n) + 1; found = dfsBacktrackFrom(sv, elapsed() + 0.1, true); if (found) break; } } } // After DFS, try to improve immediately if we have a good path if ((int)bestPath.size() >= n * 3 / 4 && (int)bestPath.size() < n) { improvePath(bestPath, min(elapsed() + 1.0, 3.0)); } if ((int)bestPath.size() < n) { // Phase 2: Warnsdorff greedy from all starts for (int s = 1; s <= n && elapsed() < 2.5; s++) { auto path = greedyPath(s, 1); if ((int)path.size() > (int)bestPath.size()) bestPath = move(path); if ((int)bestPath.size() == n) break; } } if ((int)bestPath.size() < n) { // Phase 3: Shuffled greedy while (elapsed() < 3.0 && (int)bestPath.size() < n) { for (int u = 1; u <= n; u++) { int s = adjStart[u], l = degOut[u]; for (int i = l - 1; i > 0; i--) { int j = splitmix64() % (i + 1); swap(adjData[s + i], adjData[s + j]); } s = radjStart[u]; l = degIn[u]; for (int i = l - 1; i > 0; i--) { int j = splitmix64() % (i + 1); swap(radjData[s + i], radjData[s + j]); } } int sv = randInt(n) + 1; auto path = greedyPath(sv, 2); if ((int)path.size() > (int)bestPath.size()) bestPath = move(path); if ((int)bestPath.size() == n) break; path = greedyPath(sv, 0); if ((int)path.size() > (int)bestPath.size()) bestPath = move(path); } } // Phase 4: Final improvement if ((int)bestPath.size() < n && !bestPath.empty()) improvePath(bestPath, 3.8); } else { // Large graph: DP + greedy + improve { vector order(n); for (int i = 0; i < n; i++) order[i] = i + 1; auto tryOrder = [&](vector& ord) { auto path = dpOnOrder(ord); if ((int)path.size() > (int)bestPath.size()) bestPath = path; }; sort(order.begin(), order.end(), [&](int x, int y) { int a = degOut[x] - degIn[x], b = degOut[y] - degIn[y]; return a != b ? a > b : x < y; }); tryOrder(order); sort(order.begin(), order.end(), [&](int x, int y) { return degOut[x] != degOut[y] ? degOut[x] > degOut[y] : x < y; }); tryOrder(order); for (int iter = 0; elapsed() < 1.0 && (int)bestPath.size() < n; iter++) { for (size_t i = n - 1; i > 0; --i) { size_t j = splitmix64() % (i + 1); swap(order[i], order[j]); } tryOrder(order); } } if ((int)bestPath.size() < n && !bestPath.empty()) improvePath(bestPath, 1.5); { vector starts; int best = 1; for (int u = 2; u <= n; u++) if (degOut[u] - degIn[u] > degOut[best] - degIn[best]) best = u; starts.push_back(best); best = 1; for (int u = 2; u <= n; u++) if (degIn[u] < degIn[best]) best = u; starts.push_back(best); for (int i = 0; i < 50; i++) starts.push_back(randInt(n) + 1); for (int s : starts) { if (elapsed() > 2.5 || (int)bestPath.size() == n) break; auto path = greedyPath(s, 1); if ((int)path.size() > (int)bestPath.size()) bestPath = move(path); if ((int)bestPath.size() == n) break; path = greedyPath(s, 0); if ((int)path.size() > (int)bestPath.size()) bestPath = move(path); } } if ((int)bestPath.size() < n && !bestPath.empty()) improvePath(bestPath, 3.0); if ((int)bestPath.size() < n) { while (elapsed() < 3.5 && (int)bestPath.size() < n) { for (int u = 1; u <= n; u++) { int s = adjStart[u], l = degOut[u]; for (int i = l - 1; i > 0; i--) { int j = splitmix64() % (i + 1); swap(adjData[s + i], adjData[s + j]); } s = radjStart[u]; l = degIn[u]; for (int i = l - 1; i > 0; i--) { int j = splitmix64() % (i + 1); swap(radjData[s + i], radjData[s + j]); } } auto path = greedyPath(randInt(n) + 1, 0); if ((int)path.size() > (int)bestPath.size()) { bestPath = move(path); if ((int)bestPath.size() < n) improvePath(bestPath, min(elapsed() + 0.3, 3.7)); } } } } if (bestPath.empty()) bestPath.push_back(1); printf("%d\n", (int)bestPath.size()); for (int i = 0; i < (int)bestPath.size(); i++) { if (i) putchar(' '); printf("%d", bestPath[i]); } putchar('\n'); return 0; }