File size: 10,432 Bytes
1fd0050 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | #include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <cmath>
using namespace std;
// Function to interact with the system
// ops: list of lamp IDs to toggle
// returns: vector of booleans (0 or 1) indicating adjacency status after each op
vector<int> query(const vector<int>& ops) {
if (ops.empty()) return {};
cout << ops.size();
for (int x : ops) cout << " " << x;
cout << endl;
vector<int> res(ops.size());
for (int i = 0; i < ops.size(); ++i) {
cin >> res[i];
}
return res;
}
void answer(const vector<int>& p) {
cout << "-1";
for (int x : p) cout << " " << x;
cout << endl;
exit(0);
}
int n;
int main() {
int subtask;
if (!(cin >> subtask >> n)) return 0;
// Step 1: Find 3 Independent Sets A, B, C
// We do this by finding MIS sequentially.
// Query 1: Try adding all 1..n.
// Those that don't cause conflict form I1.
// However, to clear the state for next step, we must toggle them back?
// Actually, we can just track the state.
// Let's assume we start empty.
// We'll perform batches to classify nodes.
// Batch 1: 1..n. Result gives us I1.
// But result r_i depends on current S.
// We define I1: nodes where r_i == 0.
// We know S ends up with I1 + Garbage.
// We need to clear S. The system doesn't auto-clear.
// We can clear S by toggling everything in I1 + Garbage.
// We know exactly what's in S: all i where we sent "toggle i".
// So next batch should start with clearing ops.
vector<int> p(n);
iota(p.begin(), p.end(), 1);
// Batch 1
vector<int> q1 = p;
vector<int> res1 = query(q1);
vector<int> S_state; // What is currently in S
vector<int> I1, R1;
for (int i = 0; i < n; ++i) {
if (res1[i] == 0) {
I1.push_back(p[i]);
} else {
R1.push_back(p[i]);
}
S_state.push_back(p[i]);
}
// Batch 2: Clear S, then process R1 to find I2
vector<int> q2 = S_state; // To clear
for (int x : R1) q2.push_back(x);
vector<int> res2 = query(q2);
// The first S_state.size() results are for clearing.
// The rest are for R1.
vector<int> I2, R2;
for (int i = 0; i < R1.size(); ++i) {
int r = res2[S_state.size() + i];
if (r == 0) {
I2.push_back(R1[i]);
} else {
R2.push_back(R1[i]);
}
}
// Current S contains elements from R1 processing.
// We need to clear them for the next phases.
// Current S contents: I2 + Garbage_from_R1.
// Garbage_from_R1 are elements in R1 where r==1.
// I2 are elements in R1 where r==0.
// Basically, all elements of R1 were toggled ON.
S_state = R1;
vector<int> I3 = R2; // As derived, remaining set is independent
vector<int> sets[3] = {I1, I2, I3};
vector<int> node_set_id(n + 1);
for (int x : I1) node_set_id[x] = 0;
for (int x : I2) node_set_id[x] = 1;
for (int x : I3) node_set_id[x] = 2;
// Step 2: Determine degrees between sets
// We need to distinguish if a node has 1 or 2 neighbors in a target set.
// For each u, d_0 + d_1 + d_2 = 2.
// We query each set fully to find if degree >= 1.
int degree_ge1[3][n + 1]; // [target_set][u]
// We need to clear S_state first.
// Optimize: Combine degree queries.
// Batch 3: Clear S_state. Add I1. Probe I2, I3.
// Batch 4: Clear I1 (it's in S). Add I2. Probe I1, I3.
// Batch 5: Clear I2. Add I3. Probe I1, I2.
// Actually simpler:
// Q_deg1: Clear S. Add I1. Probe All (or just R1).
// But probing modifies S. We need to restore S.
// Probe u: Add u, Check, Remove u.
vector<int> q_deg[3];
// Fill q_deg
// We need to manage S state carefully.
// Let's just assume we start each major step with clean S.
// We have S_state to clear.
// Helper to generate probe sequence
auto append_probe = [&](vector<int>& q, const vector<int>& targets) {
for (int u : targets) {
q.push_back(u);
q.push_back(u);
}
};
for (int t = 0; t < 3; ++t) {
// Clear previous state
for (int x : S_state) q_deg[t].push_back(x);
// Add target set
for (int x : sets[t]) q_deg[t].push_back(x);
S_state = sets[t];
// Probe others
vector<int> others;
for (int i = 0; i < 3; ++i) if (i != t) {
for (int x : sets[i]) others.push_back(x);
}
append_probe(q_deg[t], others);
}
// Run degree queries
for (int t = 0; t < 3; ++t) {
vector<int> res = query(q_deg[t]);
// Parse
// First part: clearing. Ignore.
// Second part: adding set t. Ignore.
// Third part: probes.
// Clearing ops count: previous S_state.size()
// Adding ops count: sets[t].size()
int offset = (int)q_deg[t].size() - 2 * (n - (int)sets[t].size());
vector<int> others;
for (int i = 0; i < 3; ++i) if (i != t) {
for (int x : sets[i]) others.push_back(x);
}
for (int i = 0; i < others.size(); ++i) {
int u = others[i];
int r = res[offset + 2 * i];
degree_ge1[t][u] = r;
}
for (int x : sets[t]) degree_ge1[t][x] = 0; // No edges within set
}
// Calculate exact degrees
int exact_deg[3][n + 1]; // [target_set][u]
for (int u = 1; u <= n; ++u) {
int my_set = node_set_id[u];
int d[3] = {0, 0, 0};
int sum_ge1 = 0;
for (int t = 0; t < 3; ++t) if (t != my_set) sum_ge1 += degree_ge1[t][u];
for (int t = 0; t < 3; ++t) {
if (t == my_set) {
exact_deg[t][u] = 0;
} else {
if (degree_ge1[t][u] == 0) exact_deg[t][u] = 0;
else {
// if sum_ge1 == 2, then we have 1 edge to each of the 2 sets (since total deg=2)
// if sum_ge1 == 1, then we have 2 edges to the one set with ge1
if (sum_ge1 == 2) exact_deg[t][u] = 1;
else exact_deg[t][u] = 2;
}
}
}
}
// Step 3: Bit queries
vector<long long> neighbor_sum(n + 1, 0);
for (int k = 0; k < 17; ++k) {
// For each set t, query intersection with M_k
// Also need intersection with NOT M_k if degree 2 exists
// We will perform 6 batches per bit:
// For each target set T:
// Query T \cap M_k
// Query T \cap !M_k
// Probing all other nodes
for (int t = 0; t < 3; ++t) {
for (int type = 0; type < 2; ++type) { // 0: M_k, 1: !M_k
vector<int> q;
// Clear S
for (int x : S_state) q.push_back(x);
// Construct target subset
vector<int> target_subset;
for (int x : sets[t]) {
int bit = (x >> k) & 1;
if ((type == 0 && bit == 1) || (type == 1 && bit == 0)) {
target_subset.push_back(x);
}
}
for (int x : target_subset) q.push_back(x);
S_state = target_subset;
// Probe others
vector<int> others;
for (int i = 0; i < 3; ++i) if (i != t) {
for (int x : sets[i]) others.push_back(x);
}
append_probe(q, others);
vector<int> res = query(q);
int offset = (int)q.size() - 2 * (int)others.size();
for (int i = 0; i < others.size(); ++i) {
int u = others[i];
int r = res[offset + 2 * i];
// Logic to update sum
// If degree is 1: we only care about type 0 (M_k). If r=1, neighbor has bit k.
// If degree is 2:
// We need sum of bits.
// S1 = res from M_k probe. S0 = res from !M_k probe.
// Sum bits = S1 + (1 - S0).
int deg = exact_deg[t][u];
if (deg == 1) {
if (type == 0 && r == 1) {
neighbor_sum[u] += (1LL << k);
}
} else if (deg == 2) {
if (type == 0) { // M_k
if (r == 1) neighbor_sum[u] += (1LL << k);
} else { // !M_k
if (r == 0) neighbor_sum[u] += (1LL << k); // +1 contribution
}
}
}
}
}
}
// Step 4: Reconstruct Ring
// Try to find one neighbor of 1
// Since we know sum_neighbors[1], if we pick neighbor v, other is sum - v.
// Validate.
for (int start_node = 2; start_node <= n; ++start_node) {
vector<int> path;
path.push_back(1);
path.push_back(start_node);
vector<bool> visited(n + 1, false);
visited[1] = true;
visited[start_node] = true;
bool possible = true;
int curr = start_node;
int prev = 1;
for (int i = 0; i < n - 2; ++i) {
long long next_sum = neighbor_sum[curr];
int next = (int)(next_sum - prev);
if (next < 1 || next > n || visited[next]) {
possible = false;
break;
}
visited[next] = true;
path.push_back(next);
prev = curr;
curr = next;
}
if (possible) {
// Check loop closure
long long last_sum = neighbor_sum[curr];
int closure = (int)(last_sum - prev);
if (closure == 1) {
// Also check consistency for 1
long long one_sum = neighbor_sum[1];
if (one_sum - curr == start_node) {
answer(path);
}
}
}
}
return 0;
} |