Problem: cf1499_g
Difficulty: medium
Categories: graph, data structures
Time limit: 7000 ms, memory limit: 1024 MB

You are given a bipartite graph consisting of $n_1$ vertices in the first part, $n_2$ vertices in the second part, and $m$ edges, numbered from $1$ to $m$. You have to color each edge into one of two colors, red and blue. You have to minimize the following value: $\sum \limits_{v \in V} |r(v) - b(v)|$, where $V$ is the set of vertices of the graph, $r(v)$ is the number of red edges incident to $v$, and $b(v)$ is the number of blue edges incident to $v$.

Sounds classical and easy, right? Well, you have to process $q$ queries of the following format:

* $1$ $v_1$ $v_2$ — add a new edge connecting the vertex $v_1$ of the first part with the vertex $v_2$ of the second part. This edge gets a new index as follows: the first added edge gets the index $m + 1$, the second — $m + 2$, and so on. After adding the edge, you have to print the hash of the current optimal coloring (if there are multiple optimal colorings, print the hash of any of them). Actually, this hash won't be verified, you may print any number as the answer to this query, but you may be asked to produce the coloring having this hash;
* $2$ — print the optimal coloring of the graph with the same hash you printed while processing the previous query. The query of this type will only be asked after a query of type $1$, and there will be at most $10$ queries of this type. If there are multiple optimal colorings corresponding to this hash, print any of them.

Note that if an edge was red or blue in some coloring, it may change its color in next colorings.

The hash of the coloring is calculated as follows: let $R$ be the set of indices of red edges, then the hash is $(\sum \limits_{i \in R} 2^i) \bmod 998244353$.

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 three integers $n_1$, $n_2$ and $m$ ($1 \le n_1, n_2, m \le 2 \cdot 10^5$).

Then $m$ lines follow, the $i$-th of them contains two integers $x_i$ and $y_i$ ($1 \le x_i \le n_1$; $1 \le y_i \le n_2$) meaning that the $i$-th edge connects the vertex $x_i$ from the first part and the vertex $y_i$ from the second part.

The next line contains one integer $q$ ($1 \le q \le 2 \cdot 10^5$) — the number of queries you have to process.

The next $q$ lines contain the queries in the format introduced in the statement.

Additional constraints on the input:

* at any moment, the graph won't contain any multiple edges;
* the queries of type $2$ are only asked if the previous query had type $1$;
* there are at most $10$ queries of type $2$.

Output

To answer a query of type $1$, print one integer — the hash of the optimal coloring.

To answer a query of type $2$, print one line. It should begin with the integer $k$ — the number of red edges. Then, $k$ distinct integer should follow — the indices of red edges in your coloring, in any order. Each index should correspond to an existing edge, and the hash of the coloring you produce should be equal to the hash you printed as the answer to the previous query.

If there are multiple answers to a query, you may print any of them.

Example

Input

```
3 4 2
1 2
3 4
10
1 1 3
1 2 3
2
1 3 3
2
1 2 4
2
1 2 1
1 1 1
2
```

Output

```
8
8
1 3
40
2 3 5
104
3 5 6 3
104
360
4 5 6 3 8
```

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


static long long queries_processed = 0;
static long long query_limit = 0;

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

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

const int MOD = 998244353;
vector<int> pow2;


void init_pow2(int max_n) {
    pow2.resize(max_n + 1);
    pow2[0] = 1;
    for (int i = 1; i <= max_n; i++) {
        pow2[i] = (1LL * pow2[i-1] * 2) % MOD;
    }
}

struct Edge {
    int u, v;
};

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

    
    int n1 = inf.readInt(1, 200000, "n1");
    int n2 = inf.readInt(1, 200000, "n2");
    int m = inf.readInt(1, 200000, "m");

    
    cout << n1 << " " << n2 << " " << m << endl;

    
    vector<Edge> edges;
    edges.reserve(400005);
    edges.push_back({-1, -1}); 

    for (int i = 1; i <= m; i++) {
        int u = inf.readInt(1, n1, "u_i");
        int v = inf.readInt(1, n2, "v_i");
        edges.push_back({u, v});
        cout << u << " " << v << endl;
    }

    int q = inf.readInt(1, 200000, "q");
    query_limit = q;
    cout << q << endl;

    
    log_metrics();

    
    init_pow2(m + q + 5);

    long long last_contestant_hash = -1;
    bool has_last_hash = false;

    for (int k = 0; k < q; k++) {
        int type = inf.readInt(1, 2, "type");
        if (type == 1) {
            int u = inf.readInt(1, n1, "u");
            int v = inf.readInt(1, n2, "v");
            
            
            cout << "1 " << u << " " << v << endl;
            
            edges.push_back({u, v});
            
            
            string token;
            if (!(cin >> token)) {
                finish(_pe, "Unexpected end of output from contestant (expected hash)");
            }
            
            long long h_val;
            try {
                h_val = stoll(token);
            } catch (...) {
                finish(_pe, "Expected integer hash, got: " + token);
            }
            
            last_contestant_hash = h_val;
            has_last_hash = true;
            queries_processed++;

        } else {
            
            cout << "2" << endl;
            
            if (!has_last_hash) {
                
                
                
                finish(_fail, "Type 2 query requested but no Type 1 query processed yet");
            }
            
            
            string token;
            if (!(cin >> token)) {
                finish(_pe, "Unexpected end of output from contestant (expected k)");
            }
            
            int num_red;
            try {
                num_red = stoi(token);
            } catch (...) {
                finish(_pe, "Expected integer k, got: " + token);
            }
            
            int current_total_edges = (int)edges.size() - 1;
            if (num_red < 0 || num_red > current_total_edges) {
                finish(_wa, "Number of red edges " + to_string(num_red) + " out of range [0, " + to_string(current_total_edges) + "]");
            }
            
            
            vector<bool> is_red(current_total_edges + 1, false);
            long long computed_hash = 0;
            
            for (int i = 0; i < num_red; i++) {
                if (!(cin >> token)) {
                    finish(_pe, "Unexpected end of output (expected red edge index)");
                }
                int idx;
                try {
                    idx = stoi(token);
                } catch (...) {
                    finish(_pe, "Expected integer edge index, got: " + token);
                }
                
                if (idx < 1 || idx > current_total_edges) {
                    finish(_wa, "Edge index " + to_string(idx) + " out of range [1, " + to_string(current_total_edges) + "]");
                }
                if (is_red[idx]) {
                    finish(_wa, "Duplicate edge index " + to_string(idx) + " in coloring");
                }
                is_red[idx] = true;
                computed_hash = (computed_hash + pow2[idx]) % MOD;
            }
            
            if (computed_hash != last_contestant_hash) {
                finish(_wa, "Coloring hash " + to_string(computed_hash) + " does not match previously declared hash " + to_string(last_contestant_hash));
            }
            
            
            
            vector<int> balance(n1 + n2 + 1, 0);
            for (int i = 1; i <= current_total_edges; i++) {
                int u = edges[i].u;
                int v = edges[i].v + n1;
                
                if (is_red[i]) {
                    balance[u]++;
                    balance[v]++;
                } else {
                    balance[u]--;
                    balance[v]--;
                }
            }
            
            for (int i = 1; i <= n1 + n2; i++) {
                if (abs(balance[i]) > 1) {
                    finish(_wa, "Vertex " + to_string(i > n1 ? i - n1 : i) + 
                           (i > n1 ? " (part 2)" : " (part 1)") + 
                           " has imbalance " + to_string(balance[i]) + 
                           " (red - blue). Allowed range [-1, 1].");
                }
            }
            
            queries_processed++;
        }
    }

    finish(_ok, "Passed " + to_string(q) + " queries");
    return 0;
}


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






static long long queries_count = 0;
static long long query_limit = 0;

const int MOD = 998244353;


struct Edge {
    int u, v; 
    int id;   
};

vector<Edge> edges;
int n1, n2;
int last_contestant_hash = -1;


vector<int> pow2;


void ensure_pow2(int max_idx) {
    if (pow2.empty()) {
        pow2.push_back(1); 
    }
    while ((int)pow2.size() <= max_idx) {
        pow2.push_back((1LL * pow2.back() * 2) % MOD);
    }
}


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


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


long long readContestantHash() {
    return ouf.readLong(0LL, 2000000000LL, "hash");
}


void verifyColoring() {
    int current_m = edges.size();
    
    
    int k = ouf.readInt(0, current_m, "k (number of red edges)");
    
    
    ensure_pow2(current_m);

    vector<bool> is_red(current_m + 1, false);
    long long computed_hash = 0;
    
    for (int i = 0; i < k; i++) {
        int idx = ouf.readInt(1, current_m, "edge index");
        if (is_red[idx]) {
            finish(_wa, "Duplicate edge index in coloring: " + to_string(idx));
        }
        is_red[idx] = true;
        computed_hash = (computed_hash + pow2[idx]) % MOD;
    }
    
    
    if (computed_hash != last_contestant_hash) {
        finish(_wa, "Hash mismatch. Expected: " + to_string(last_contestant_hash) + ", Found: " + to_string(computed_hash));
    }
    
    
    
    vector<int> r(n1 + n2 + 1, 0);
    vector<int> b(n1 + n2 + 1, 0);
    
    for (int i = 0; i < current_m; i++) {
        int idx = edges[i].id;
        int u = edges[i].u;
        int v_shifted = edges[i].v + n1;
        
        if (is_red[idx]) {
            r[u]++;
            r[v_shifted]++;
        } else {
            b[u]++;
            b[v_shifted]++;
        }
    }
    
    for (int i = 1; i <= n1 + n2; i++) {
        if (abs(r[i] - b[i]) > 1) {
            string v_str = (i > n1) ? ("(part 2 vertex " + to_string(i - n1) + ")") 
                                    : ("(part 1 vertex " + to_string(i) + ")");
            finish(_wa, "Coloring not optimal at " + v_str + 
                        ". r=" + to_string(r[i]) + ", b=" + to_string(b[i]));
        }
    }
}

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

    
    n1 = inf.readInt();
    n2 = inf.readInt();
    int m_initial = inf.readInt();
    
    
    cout << n1 << " " << n2 << " " << m_initial << endl;
    
    
    for (int i = 1; i <= m_initial; i++) {
        int u = inf.readInt();
        int v = inf.readInt();
        edges.push_back({u, v, i});
        cout << u << " " << v << endl;
    }
    
    
    int q = inf.readInt();
    cout << q << endl;
    
    query_limit = q;
    ensure_pow2(1); 

    
    for (int k = 0; k < q; k++) {
        int type = inf.readInt();
        
        if (type == 1) {
            int u = inf.readInt();
            int v = inf.readInt();
            
            
            cout << "1 " << u << " " << v << endl;
            
            
            int new_idx = (int)edges.size() + 1;
            edges.push_back({u, v, new_idx});
            
            
            long long h = readContestantHash();
            last_contestant_hash = (int)h;
        } else {
            
            cout << "2" << endl;
            
            
            verifyColoring();
        }
        
        queries_count++;
        
        if (queries_count % 1000 == 0) log_metrics();
    }
    
    finish(_ok, "All queries processed successfully");
}


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 seed = atoi(argv[1]);

    
    int n1, n2, m, q;

    
    if (seed == 1) {
        n1 = 3; n2 = 4; m = 2; q = 10;
    } else if (seed == 2) {
        
        n1 = 2; n2 = 2; m = 1; q = 5;
    } else if (seed == 3) {
        
        n1 = 10; n2 = 10; m = 20; q = 50;
    } else {
        
        int n1_min = 100000, n1_max = 200000;
        int n2_min = 100000, n2_max = 200000;
        int m_min = 100000, m_max = 200000;
        int q_min = 100000, q_max = 200000;

        n1 = opt<int>("n1", rnd.next(n1_min, n1_max));
        n2 = opt<int>("n2", rnd.next(n2_min, n2_max));
        m = opt<int>("m", rnd.next(m_min, m_max));
        q = opt<int>("q", rnd.next(q_min, q_max));
    }

    
    n1 = max(1, n1);
    n2 = max(1, n2);
    m = max(1, m);
    q = max(1, q);

    long long max_edges = (long long)n1 * n2;
    
    
    
    if (m > max_edges) m = (int)max_edges;
    if (m == max_edges && max_edges > 1) m--; 

    vector<pair<int, int>> initial_edges;
    vector<string> queries;

    
    
    if (max_edges <= 2000000) {
        vector<pair<int, int>> pool;
        pool.reserve(max_edges);
        for (int i = 1; i <= n1; i++) {
            for (int j = 1; j <= n2; j++) {
                pool.push_back({i, j});
            }
        }
        shuffle(pool.begin(), pool.end());

        int ptr = 0;
        
        for (int i = 0; i < m && ptr < (int)pool.size(); i++) {
            initial_edges.push_back(pool[ptr++]);
        }
        m = (int)initial_edges.size(); 

        
        int t2_count = 0;
        bool last_type1 = false;
        
        for (int k = 0; k < q; k++) {
            
            bool type2 = false;
            
            if (last_type1 && t2_count < 10) {
                
                if (rnd.next(20) == 0 || ptr >= (int)pool.size()) {
                    type2 = true;
                }
            }

            
            if (!type2 && ptr >= (int)pool.size()) {
                 break; 
            }

            if (type2) {
                queries.push_back("2");
                t2_count++;
                last_type1 = false;
            } else {
                pair<int, int> e = pool[ptr++];
                queries.push_back("1 " + to_string(e.first) + " " + to_string(e.second));
                last_type1 = true;
            }
        }
    } else {
        
        set<long long> used;
        auto get_hash = [&](int u, int v) {
            return ((long long)u << 32) | v;
        };

        
        while ((int)initial_edges.size() < m) {
            int u = rnd.next(1, n1);
            int v = rnd.next(1, n2);
            if (used.insert(get_hash(u, v)).second) {
                initial_edges.push_back({u, v});
            }
        }

        
        
        vector<int> hot_n1, hot_n2;
        if (mode == "adp") {
            int cnt1 = max(1, n1 / 40);
            int cnt2 = max(1, n2 / 40);
            for(int i=0; i<cnt1; ++i) hot_n1.push_back(rnd.next(1, n1));
            for(int i=0; i<cnt2; ++i) hot_n2.push_back(rnd.next(1, n2));
        }

        int t2_count = 0;
        bool last_type1 = false;

        for (int k = 0; k < q; k++) {
            bool type2 = false;
            if (last_type1 && t2_count < 10 && rnd.next(20) == 0) {
                type2 = true;
            }

            if (type2) {
                queries.push_back("2");
                t2_count++;
                last_type1 = false;
            } else {
                
                while (true) {
                    int u, v;
                    
                    if (mode == "adp" && !hot_n1.empty() && rnd.next(3) == 0)
                        u = hot_n1[rnd.next((int)hot_n1.size())];
                    else
                        u = rnd.next(1, n1);

                    if (mode == "adp" && !hot_n2.empty() && rnd.next(3) == 0)
                        v = hot_n2[rnd.next((int)hot_n2.size())];
                    else
                        v = rnd.next(1, n2);

                    if (used.insert(get_hash(u, v)).second) {
                        queries.push_back("1 " + to_string(u) + " " + to_string(v));
                        break;
                    }
                    
                }
                last_type1 = true;
            }
        }
    }

    
    cout << n1 << " " << n2 << " " << m << "\n";
    for (auto &p : initial_edges) {
        cout << p.first << " " << p.second << "\n";
    }
    cout << queries.size() << "\n";
    for (auto &s : queries) {
        cout << s << "\n";
    }

    return 0;
}