Problem: cf1633_f
Difficulty: hard
Categories: graph, data structures
Time limit: 12000 ms, memory limit: 512 MB

You are given a tree consisting of $n$ vertices (numbered from $1$ to $n$) and $n-1$ edges (numbered from $1$ to $n-1$). Initially, all vertices except vertex $1$ are inactive.

You have to process queries of three types:

* $1$ $v$ — activate the vertex $v$. It is guaranteed that the vertex $v$ is inactive before this query, and one of its neighbors is active. After activating the vertex, you have to choose a subset of edges of the tree such that each active vertex is incident to exactly one chosen edge, and each inactive vertex is not incident to any of the chosen edges — in other words, this subset should represent a perfect matching on the active part of the tree. If any such subset of edges exists, print the sum of indices of edges in it; otherwise, print $0$.
* $2$ — queries of this type will be asked only right after a query of type $1$, and there will be at most $10$ such queries. If your answer to the previous query was $0$, simply print $0$; otherwise, print the subset of edges for the previous query as follows: first, print the number of edges in the subset, then print the indices of the chosen edges in ascending order. The sum of indices should be equal to your answer to the previous query.
* $3$ — terminate the program.

Note that you should solve the problem in online mode. It means that you can't read the whole input at once. You can read each query only after writing the answer for the last query. Use functions fflush in C++ and BufferedWriter.flush in Java languages after each writing in your program.

Input

The first line contains one integer $n$ ($2 \le n \le 2 \cdot 10^5$) — the number of vertices of the tree.

Then $n-1$ lines follow. The $i$-th line contains two integers $u_i$ and $v_i$ ($1 \le u_i, v_i \le n$; $u_i \ne v_i$) — the endpoints of the $i$-th edge. These edges form a tree.

Then the queries follow in the format described in the statement, one line per query. There will be at least $2$ and at most $n+10$ queries. The last query (and only the last one) will be of type $3$. Note that you can read the $i$-th query only if you have already given the answer for the query $i-1$ (except for $i = 1$).

If your answer for one of the queries is incorrect and the judging program recognizes it, instead of the next query, you may receive the integer $0$ on a separate line. After receiving it, your program should terminate gracefully, and you will receive "Wrong Answer" verdict. If your program doesn't terminate, your solution may receive some other verdict, like "Time Limit Exceeded", "Idleness Limit Exceeded", etc. Note that the fact that your solution doesn't receive the integer $0$, it does not mean that all your answers are correct, some of them will be checked only after your program is terminated.

Output

For each query of type $1$ or $2$, print the answer on a separate line as described in the statement. Don't forget to flush the output.

Example

Input

```
6
1 4
6 1
3 2
1 2
5 1
1 4
2
1 2
2
1 3
2
1 5
1 6
2
3
```

Output

```
1
1 1
0
0
4
2 1 3
0
0
0
```

Non-adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;


static long long queries = 0;
static long long query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << queries << endl;
        tout << "query_limit=" << query_limit << endl;
    } catch (...) {
        
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

static void bad_answer(const string& reason) {
    
    
    cout << "0" << endl;
    finish(_wa, reason);
}





const int MAXN = 200005;


vector<pair<int, int>> adj[MAXN];
pair<int, int> input_edges[MAXN];
int parent_edge_index[MAXN]; 


int par[MAXN], sz[MAXN], depth[MAXN];
int head[MAXN], tin[MAXN], rin[MAXN]; 
int timer_hld;

void dfs_sz(int u, int p, int d) {
    sz[u] = 1;
    par[u] = p;
    depth[u] = d;
    for (auto& edge : adj[u]) {
        int v = edge.first;
        int id = edge.second;
        if (v != p) {
            parent_edge_index[v] = id;
            dfs_sz(v, u, d + 1);
            sz[u] += sz[v];
        }
    }
}

void dfs_hld(int u, int h) {
    head[u] = h;
    tin[u] = ++timer_hld;
    rin[timer_hld] = u;
    
    int heavy_child = -1, max_sz = -1;
    for (auto& edge : adj[u]) {
        int v = edge.first;
        if (v != par[u]) {
            if (sz[v] > max_sz) {
                max_sz = sz[v];
                heavy_child = v;
            }
        }
    }
    
    if (heavy_child != -1) dfs_hld(heavy_child, h);
    
    for (auto& edge : adj[u]) {
        int v = edge.first;
        if (v != par[u] && v != heavy_child) {
            dfs_hld(v, v);
        }
    }
}







struct Node {
    int cnt;                   
    long long val;             
    long long total_indices;   
    bool flip;                 
} tree[4 * MAXN];

void push(int v, int tl, int tr) {
    if (tree[v].flip) {
        int tm = (tl + tr) / 2;
        int left = 2 * v;
        int right = 2 * v + 1;
        
        tree[left].flip = !tree[left].flip;
        tree[left].cnt = (tm - tl + 1) - tree[left].cnt;
        tree[left].val = tree[left].total_indices - tree[left].val;
        
        tree[right].flip = !tree[right].flip;
        tree[right].cnt = (tr - tm) - tree[right].cnt;
        tree[right].val = tree[right].total_indices - tree[right].val;
        
        tree[v].flip = false;
    }
}

void pull(int v) {
    tree[v].cnt = tree[2 * v].cnt + tree[2 * v + 1].cnt;
    tree[v].val = tree[2 * v].val + tree[2 * v + 1].val;
    tree[v].total_indices = tree[2 * v].total_indices + tree[2 * v + 1].total_indices;
}

void build(int v, int tl, int tr) {
    tree[v].flip = false;
    tree[v].cnt = 0;
    tree[v].val = 0;
    if (tl == tr) {
        int u = rin[tl];
        
        
        if (u != 1) tree[v].total_indices = parent_edge_index[u];
        else tree[v].total_indices = 0;
    } else {
        int tm = (tl + tr) / 2;
        build(2 * v, tl, tm);
        build(2 * v + 1, tm + 1, tr);
        pull(v);
    }
}

void update(int v, int tl, int tr, int l, int r) {
    if (l > r) return;
    if (l == tl && r == tr) {
        tree[v].flip = !tree[v].flip;
        tree[v].cnt = (tr - tl + 1) - tree[v].cnt;
        tree[v].val = tree[v].total_indices - tree[v].val;
    } else {
        push(v, tl, tr);
        int tm = (tl + tr) / 2;
        update(2 * v, tl, tm, l, min(r, tm));
        update(2 * v + 1, tm + 1, tr, max(l, tm + 1), r);
        pull(v);
    }
}

void collect(int v, int tl, int tr, vector<int>& out) {
    if (tree[v].cnt == 0) return;
    if (tl == tr) {
        int u = rin[tl];
        if (u != 1) out.push_back(parent_edge_index[u]);
        return;
    }
    push(v, tl, tr);
    int tm = (tl + tr) / 2;
    collect(2 * v, tl, tm, out);
    collect(2 * v + 1, tm + 1, tr, out);
}

void path_update(int u) {
    
    while (u != -1) {
        int top_node = head[u];
        if (top_node == 1) {
            
            
            
            
            if (u != 1) update(1, 1, timer_hld, tin[1] + 1, tin[u]);
            break;
        }
        update(1, 1, timer_hld, tin[top_node], tin[u]);
        u = par[top_node];
    }
}





int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    atexit(log_metrics);

    
    int n = inf.readInt();
    query_limit = n + 200; 

    for (int i = 0; i < n - 1; ++i) {
        int u = inf.readInt();
        int v = inf.readInt();
        adj[u].push_back({v, i + 1});
        adj[v].push_back({u, i + 1});
        input_edges[i] = {u, v};
    }

    
    timer_hld = 0;
    dfs_sz(1, -1, 0);
    dfs_hld(1, 1);
    
    
    build(1, 1, n);

    
    cout << n << endl;
    for (int i = 0; i < n - 1; ++i) {
        cout << input_edges[i].first << " " << input_edges[i].second << endl;
    }

    int active_cnt = 1; 

    
    while (!inf.eof()) {
        inf.skipBlanks();
        if (inf.eof()) break;

        
        if (queries % 1000 == 0) log_metrics();

        int type = inf.readInt();
        
        if (type == 3) {
            queries++;
            cout << "3" << endl;
            break; 
        } 
        else if (type == 1) {
            queries++;
            int v = inf.readInt();
            cout << "1 " << v << endl;

            
            path_update(v);
            active_cnt++;
            
            
            
            
            
            
            
            bool exists = false;
            long long expected_ans = 0;
            
            if (active_cnt % 2 == 0 && tree[1].cnt == active_cnt / 2) {
                exists = true;
                expected_ans = tree[1].val;
            }

            
            long long ans;
            if (!(cin >> ans)) finish(_pe, "Expected integer answer for Type 1");
            
            if (exists) {
                if (ans != expected_ans) {
                    bad_answer("Expected " + to_string(expected_ans) + " but got " + to_string(ans));
                }
            } else {
                if (ans != 0) {
                    bad_answer("Expected 0 (no matching) but got " + to_string(ans));
                }
            }
        } 
        else if (type == 2) {
            queries++;
            cout << "2" << endl;

            
            bool exists = (active_cnt % 2 == 0 && tree[1].cnt == active_cnt / 2);
            
            if (!exists) {
                
                long long k;
                if (!(cin >> k)) finish(_pe, "Expected integer size for Type 2");
                if (k != 0) bad_answer("Expected size 0 for Type 2 when no matching exists");
            } else {
                
                int k;
                if (!(cin >> k)) finish(_pe, "Expected integer size for Type 2");
                
                int expected_size = active_cnt / 2;
                if (k != expected_size) {
                    bad_answer("Expected size " + to_string(expected_size) + " but got " + to_string(k));
                }
                
                vector<int> user_edges(k);
                for (int i = 0; i < k; ++i) {
                    if (!(cin >> user_edges[i])) finish(_pe, "Expected edge index");
                }
                
                
                vector<int> expected_edges;
                collect(1, 1, n, expected_edges);
                
                
                sort(user_edges.begin(), user_edges.end());
                sort(expected_edges.begin(), expected_edges.end());
                
                if (user_edges != expected_edges) {
                    bad_answer("Edges set does not match expected perfect matching");
                }
            }
        }
    }

    finish(_ok, "OK");
    return 0;
}


Adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;


const int MAXN = 200005;


vector<pair<int, int>> adj[MAXN];
int parent_node[MAXN], depth[MAXN], sz[MAXN];
int head[MAXN], pos[MAXN];
long long edge_index_at_pos[MAXN]; 
int cur_pos = 1;
int n;


static long long queries_cnt = 0;
static long long query_limit_val = 0;


void log_metrics() {
    tout << "queries=" << queries_cnt << endl;
    tout << "query_limit=" << query_limit_val << endl;
}


template<typename... Args>
void finish(TResult verdict, const char* fmt, Args... args) {
    log_metrics(); 
    quitf(verdict, fmt, args...);
}





struct NodeData {
    int cnt;        
    long long sum;  
    int cap;        
    long long tot_w;
};

NodeData tree_dat[4 * MAXN];
bool lazy_tag[4 * MAXN];

NodeData merge(const NodeData& a, const NodeData& b) {
    return {a.cnt + b.cnt, a.sum + b.sum, a.cap + b.cap, a.tot_w + b.tot_w};
}

void build(int v, int tl, int tr) {
    if (tl == tr) {
        
        
        if (edge_index_at_pos[tl] > 0) {
            tree_dat[v] = {0, 0, 1, edge_index_at_pos[tl]};
        } else {
            tree_dat[v] = {0, 0, 0, 0};
        }
        lazy_tag[v] = false;
    } else {
        int tm = (tl + tr) / 2;
        build(2*v, tl, tm);
        build(2*v+1, tm+1, tr);
        tree_dat[v] = merge(tree_dat[2*v], tree_dat[2*v+1]);
        lazy_tag[v] = false;
    }
}

void apply_node(int v) {
    
    
    
    tree_dat[v].cnt = tree_dat[v].cap - tree_dat[v].cnt;
    tree_dat[v].sum = tree_dat[v].tot_w - tree_dat[v].sum;
    lazy_tag[v] = !lazy_tag[v];
}

void push(int v) {
    if (lazy_tag[v]) {
        apply_node(2*v);
        apply_node(2*v+1);
        lazy_tag[v] = false;
    }
}

void update(int v, int tl, int tr, int l, int r) {
    if (l > r) return;
    if (l == tl && r == tr) {
        apply_node(v);
    } else {
        push(v);
        int tm = (tl + tr) / 2;
        update(2*v, tl, tm, l, min(r, tm));
        update(2*v+1, tm+1, tr, max(l, tm+1), r);
        tree_dat[v] = merge(tree_dat[2*v], tree_dat[2*v+1]);
    }
}


vector<int> collected_edges;
void collect(int v, int tl, int tr) {
    if (tree_dat[v].cnt == 0) return; 
    if (tl == tr) {
        if (tree_dat[v].cnt == 1) {
            collected_edges.push_back((int)tree_dat[v].tot_w);
        }
    } else {
        push(v);
        int tm = (tl + tr) / 2;
        collect(2*v, tl, tm);
        collect(2*v+1, tm+1, tr);
    }
}


void dfs_sz(int u, int p) {
    sz[u] = 1;
    parent_node[u] = p;
    depth[u] = depth[p] + 1;
    for (auto& edge : adj[u]) {
        int v = edge.first;
        if (v != p) {
            dfs_sz(v, u);
            sz[u] += sz[v];
        }
    }
}

void dfs_hld(int u, int h, int edge_idx) {
    head[u] = h;
    pos[u] = cur_pos++;
    edge_index_at_pos[pos[u]] = edge_idx;
    
    int heavy_child = -1, max_sz = -1, heavy_idx = -1;
    for (auto& edge : adj[u]) {
        int v = edge.first;
        if (v != parent_node[u]) {
            if (sz[v] > max_sz) {
                max_sz = sz[v];
                heavy_child = v;
                heavy_idx = edge.second;
            }
        }
    }
    
    if (heavy_child != -1) {
        dfs_hld(heavy_child, h, heavy_idx);
    }
    
    for (auto& edge : adj[u]) {
        int v = edge.first;
        if (v != parent_node[u] && v != heavy_child) {
            dfs_hld(v, v, edge.second);
        }
    }
}


void path_update(int u) {
    while (u != 0) {
        update(1, 1, n, pos[head[u]], pos[u]);
        u = parent_node[head[u]];
    }
}


int main(int argc, char* argv[]) {
    registerInteraction(argc, argv);
    atexit(log_metrics); 

    n = inf.readInt();
    
    query_limit_val = n + 10;
    
    
    cout << n << endl;

    
    for (int i = 0; i < n - 1; i++) {
        int u = inf.readInt();
        int v = inf.readInt();
        adj[u].push_back({v, i + 1});
        adj[v].push_back({u, i + 1});
        cout << u << " " << v << endl;
    }
    
    
    depth[0] = 0; 
    dfs_sz(1, 0);
    dfs_hld(1, 1, 0);
    build(1, 1, n);

    
    vector<int> candidates;
    vector<bool> active(n + 1, false);
    vector<bool> is_candidate(n + 1, false);
    
    active[1] = true;
    for (auto& p : adj[1]) {
        candidates.push_back(p.first);
        is_candidate[p.first] = true;
    }
    
    int active_count = 1;
    int type2_left = 10;
    
    log_metrics(); 

    
    while (active_count < n && !candidates.empty()) {
        
        if (queries_cnt >= query_limit_val - 1) break;
        
        
        int idx = rnd.next(0, (int)candidates.size() - 1);
        int u = candidates[idx];
        
        candidates[idx] = candidates.back();
        candidates.pop_back();
        is_candidate[u] = false;
        
        
        cout << "1 " << u << endl;
        queries_cnt++;
        if (queries_cnt % 1000 == 0) log_metrics(); 
        
        
        active[u] = true;
        active_count++;
        
        for (auto& edge : adj[u]) {
            int v = edge.first;
            if (!active[v] && !is_candidate[v]) {
                candidates.push_back(v);
                is_candidate[v] = true;
            }
        }
        
        path_update(u);
        
        
        bool pm_exists = false;
        long long expected_sum = 0;
        
        if (active_count % 2 == 0) {
            
            if (tree_dat[1].cnt == active_count / 2) {
                pm_exists = true;
                expected_sum = tree_dat[1].sum;
            }
        }
        
        
        long long user_ans;
        if (!(cin >> user_ans)) finish(_pe, "Unexpected EOF reading type 1 answer");
        
        if (pm_exists) {
            if (user_ans != expected_sum) {
                cout << "0" << endl;
                finish(_wa, "Type 1: expected %lld, got %lld", expected_sum, user_ans);
            }
        } else {
            if (user_ans != 0) {
                cout << "0" << endl;
                finish(_wa, "Type 1: expected 0, got %lld", user_ans);
            }
        }
        
        
        if (type2_left > 0 && queries_cnt < query_limit_val - 1) {
            bool ask = false;
            
            if (pm_exists) {
                
                if (rnd.next(0, 2) == 0) ask = true;
            } else {
                if (rnd.next(0, 9) == 0) ask = true;
            }
            
            if (n - active_count <= type2_left) ask = true;
            
            if (ask) {
                cout << "2" << endl;
                queries_cnt++;
                type2_left--;
                
                if (!pm_exists) {
                    long long k;
                    if (!(cin >> k)) finish(_pe, "EOF reading type 2 answer");
                    if (k != 0) {
                        cout << "0" << endl;
                        finish(_wa, "Type 2: expected 0 (no PM), got %lld", k);
                    }
                } else {
                    int k;
                    if (!(cin >> k)) finish(_pe, "EOF reading type 2 size");
                    int expected_sz = active_count / 2;
                    if (k != expected_sz) {
                        cout << "0" << endl;
                        finish(_wa, "Type 2: expected size %d, got %d", expected_sz, k);
                    }
                    
                    collected_edges.clear();
                    collect(1, 1, n);
                    sort(collected_edges.begin(), collected_edges.end());
                    
                    for (int i = 0; i < k; i++) {
                        int val;
                        if (!(cin >> val)) finish(_pe, "EOF reading type 2 indices");
                        if (val != collected_edges[i]) {
                            cout << "0" << endl;
                            finish(_wa, "Type 2: mismatch at pos %d. Exp %d, got %d", i, collected_edges[i], val);
                        }
                    }
                }
            }
        }
    }
    
    
    cout << "3" << endl;
    finish(_ok, "Processed %lld queries correctly", queries_cnt);
}


Test generator (C++):
#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);

    string mode = opt<string>("mode", "non");
    int n_opt = opt<int>("n", -1);

    int n;
    if (n_opt != -1) {
        n = n_opt;
    } else {
        
        int type = rnd.next(1, 20);
        if (type <= 2) n = rnd.next(2, 10);              
        else if (type <= 5) n = rnd.next(11, 100);       
        else if (type <= 8) n = rnd.next(101, 5000);     
        else n = rnd.next(100000, 200000);               
    }

    
    
    
    
    
    
    
    
    int shape = rnd.next(0, 4);
    vector<pair<int, int>> edges;
    vector<vector<int>> adj(n + 1);

    auto add_edge = [&](int u, int v) {
        edges.push_back({u, v});
        adj[u].push_back(v);
        adj[v].push_back(u);
    };

    
    vector<int> p(n);
    iota(p.begin(), p.end(), 1);
    shuffle(p.begin(), p.end());
    
    
    
    
    
    
    
    
    
    
    if (shape == 0) { 
        
        for (int i = 1; i < n; i++) {
            int u = p[i];
            int v = p[rnd.next(0, i - 1)];
            add_edge(u, v);
        }
    } else if (shape == 1) { 
        
        for(int i = 0; i < n - 1; ++i) add_edge(p[i], p[i+1]);
    } else if (shape == 2) { 
        
        int center = p[0];
        for(int i = 1; i < n; ++i) add_edge(center, p[i]);
    } else if (shape == 3) { 
        
        int len = rnd.next(2, max(2, n / 2));
        for(int i = 0; i < len - 1; ++i) add_edge(p[i], p[i+1]);
        for(int i = len; i < n; ++i) {
            
            add_edge(p[i], p[rnd.next(0, len - 1)]);
        }
    } else { 
        
        for (int i = 1; i < n; i++) {
            int u = p[i];
            int window = min(i, 20); 
            int v = p[rnd.next(i - window, i - 1)];
            add_edge(u, v);
        }
    }

    
    cout << n << "\n";
    
    for (auto& e : edges) {
        cout << e.first << " " << e.second << "\n";
    }

    
    
    if (mode == "adp") {
        return 0;
    }

    
    
    
    vector<bool> active(n + 1, false);
    active[1] = true;
    
    vector<int> candidates;
    vector<bool> is_candidate(n + 1, false);
    
    for (int v : adj[1]) {
        candidates.push_back(v);
        is_candidate[v] = true;
    }
    
    int type2_left = 10;
    int activations = 0;
    
    while (activations < n - 1 && !candidates.empty()) {
        
        
        int idx = rnd.next(0, (int)candidates.size() - 1);
        int u = candidates[idx];
        
        
        candidates[idx] = candidates.back();
        candidates.pop_back();
        is_candidate[u] = false;
        
        
        cout << "1 " << u << "\n";
        active[u] = true;
        activations++;
        
        
        for (int v : adj[u]) {
            if (!active[v] && !is_candidate[v]) {
                candidates.push_back(v);
                is_candidate[v] = true;
            }
        }
        
        
        if (type2_left > 0) {
            int remaining_activations = (n - 1) - activations;
            
            
            if (remaining_activations == 0 || rnd.next(0, remaining_activations + 5) < type2_left * 2) {
                cout << "2\n";
                type2_left--;
            }
        }
    }
    
    
    cout << "3\n";

    return 0;
}