Problem: cf1721_f
Difficulty: medium
Categories: graph
Time limit: 8000 ms, memory limit: 512 MB

You are given a bipartite graph with $n_1$ vertices in the first part, $n_2$ vertices in the second part, and $m$ edges. The maximum matching in this graph is the maximum possible (by size) subset of edges of this graph such that no vertex is incident to more than one chosen edge.

You have to process two types of queries to this graph:

* $1$ — remove the minimum possible number of vertices from this graph so that the size of the maximum matching gets reduced exactly by $1$, and print the vertices that you have removed. Then, find any maximum matching in this graph and print the sum of indices of edges belonging to this matching;
* $2$ — query of this type will be asked only after a query of type $1$. As the answer to this query, you have to print the edges forming the maximum matching you have chosen in the previous query.

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 four integers $n_1$, $n_2$, $m$ and $q$ ($1 \le n_1, n_2 \le 2 \cdot 10^5$; $1 \le m \le \min(n_1 \cdot n_2, 2 \cdot 10^5)$; $1 \le q \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$ in the first part and the vertex $y_i$ in the second part. There are no pairs of vertices that are connected by more than one edge.

Then $q$ lines follow. The $i$-th of them contains one integer, $1$ or $2$, denoting the $i$-th query. Additional constraints on queries:

* the number of queries of type $1$ won't exceed the size of the maximum matching in the initial graph;
* the number of queries of type $2$ won't exceed $3$;
* each query of type $2$ is preceded by a query of type $1$;
* your solution is allowed to read the $i$-th query only after printing the answer for the $(i-1)$-th query and flushing the output.

Output

For a query of type $1$, print the answer in three lines as follows:

* the first line should contain the number of vertices you remove;
* the second line should contain the indices of vertices you remove, as follows: if you remove the vertex $x$ from the left part, print $x$; if you remove the vertex $y$ from the right part, print $-y$ (negative index);
* the third line should contain the sum of indices of edges in some maximum matching in the resulting graph. The edges are numbered from $1$ to $m$.

For a query of type $2$, print the answer in two lines as follows:

* the first line should contain the size of the maximum matching;
* the second line should contain the indices of the edges belonging to the maximum matching. Note that the sum of these indices should be equal to the number you printed at the end of the previous query of type $1$;

After printing the answer to a query, don't forget to flush the output.

Example

Input

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

Output

```
1
-4
3
===
2
1 2
===
1
2
2
===
1
2
```

Note

In this problem, you may receive the verdict "Idleness Limit Exceeded" since it is in online mode. If it happens, it means that either the output format is wrong, or you don't meet some constraint of the problem. You may treat this verdict as "Wrong Answer".

For your convenience, the output for queries in the example is separated by the line ===. Don't print this line in your program, it is done only to make sure that it's easy to distinguish between answers for different queries in the statement.

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


struct Edge {
    int to;
    int rev;
    int flow;
    int cap;
};

vector<vector<Edge>> adj;
vector<int> level;
vector<int> ptr;

void add_edge(int from, int to, int cap) {
    adj[from].push_back({to, (int)adj[to].size(), 0, cap});
    adj[to].push_back({from, (int)adj[from].size() - 1, 0, 0});
}

bool bfs(int s, int t) {
    fill(level.begin(), level.end(), -1);
    level[s] = 0;
    queue<int> q;
    q.push(s);
    while (!q.empty()) {
        int v = q.front();
        q.pop();
        for (auto& edge : adj[v]) {
            if (edge.cap - edge.flow > 0 && level[edge.to] == -1) {
                level[edge.to] = level[v] + 1;
                q.push(edge.to);
            }
        }
    }
    return level[t] != -1;
}

int dfs(int v, int t, int pushed) {
    if (pushed == 0) return 0;
    if (v == t) return pushed;
    for (int& cid = ptr[v]; cid < adj[v].size(); ++cid) {
        auto& edge = adj[v][cid];
        int tr = edge.to;
        if (level[v] + 1 != level[tr] || edge.cap - edge.flow == 0) continue;
        int push = dfs(tr, t, min(pushed, edge.cap - edge.flow));
        if (push == 0) continue;
        edge.flow += push;
        adj[tr][edge.rev].flow -= push;
        return push;
    }
    return 0;
}

int dinic(int s, int t) {
    int flow = 0;
    while (bfs(s, t)) {
        fill(ptr.begin(), ptr.end(), 0);
        while (int pushed = dfs(s, t, 1e9)) {
            flow += pushed;
        }
    }
    return flow;
}


int n1, n2, m, q;
vector<pair<int, int>> edges;
vector<bool> is_removed;
vector<bool> in_mvc;
vector<int> D; 
vector<vector<int>> g_adj; 


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());
}

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

    
    n1 = inf.readInt();
    n2 = inf.readInt();
    m = inf.readInt();
    q = inf.readInt();

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

    
    edges.resize(m + 1);
    g_adj.resize(n1 + n2 + 1);

    
    int S = 0, T = n1 + n2 + 1;
    adj.resize(T + 1);
    level.resize(T + 1);
    ptr.resize(T + 1);

    for (int i = 1; i <= n1; i++) add_edge(S, i, 1);
    for (int i = 1; i <= n2; i++) add_edge(n1 + i, T, 1);

    for (int i = 1; i <= m; i++) {
        int u = inf.readInt();
        int v = inf.readInt();
        edges[i] = {u, v};

        
        cout << u << " " << v << endl;

        
        add_edge(u, n1 + v, 1);
        g_adj[u].push_back(n1 + v);
        g_adj[n1 + v].push_back(u);
    }
    cout.flush();

    
    vector<int> query_types(q);
    for (int i = 0; i < q; i++) {
        query_types[i] = inf.readInt();
    }

    
    int max_matching = dinic(S, T);
    query_limit = max_matching; 
    log_metrics(); 

    
    
    vector<bool> reachable(T + 1, false);
    queue<int> Q;
    Q.push(S);
    reachable[S] = true;

    while(!Q.empty()){
        int u = Q.front(); Q.pop();
        for(auto& e : adj[u]){
            if(e.cap - e.flow > 0 && !reachable[e.to]){
                reachable[e.to] = true;
                Q.push(e.to);
            }
        }
    }

    
    in_mvc.assign(n1 + n2 + 1, false);
    is_removed.assign(n1 + n2 + 1, false);

    int mvc_size = 0;
    for(int i=1; i<=n1; ++i) {
        if(!reachable[i]) { in_mvc[i] = true; mvc_size++; }
    }
    for(int i=1; i<=n2; ++i) {
        if(reachable[n1 + i]) { in_mvc[n1 + i] = true; mvc_size++; }
    }
    
    if(mvc_size != max_matching) {
        
        
    }

    
    D.assign(n1 + n2 + 1, 0);
    for(int i=1; i<=n1+n2; ++i) {
        if(in_mvc[i]) {
            for(int neighbor : g_adj[i]) {
                if(!in_mvc[neighbor]) { 
                    D[i]++;
                }
            }
        }
    }

    int current_matching_size = max_matching;

    
    for (int k = 0; k < q; k++) {
        
        if (k % 50 == 0) log_metrics();

        int type = query_types[k];
        cout << type << endl; 

        if (type == 1) {
            queries++;
            
            
            string line1;
            
            while(line1.empty()) {
                if(!(cin >> line1)) finish(_wa, "Unexpected EOF reading answer to type 1");
            }
            
            
            if (line1[0] == '!') finish(_wa, "Unexpected answer format (received '!')");

            int count = 0;
            try { count = stoi(line1); } catch(...) { finish(_wa, "Invalid integer for count"); }

            if (count != 1) finish(_wa, "Count of removed vertices must be 1");

            int idx = 0;
            if(!(cin >> idx)) finish(_wa, "Unexpected EOF reading vertex index");

            
            int u = 0;
            if (idx > 0) { 
                u = idx;
                if (u < 1 || u > n1) finish(_wa, "Vertex index out of bounds");
            } else if (idx < 0) { 
                u = n1 + (-idx);
                if (u <= n1 || u > n1 + n2) finish(_wa, "Vertex index out of bounds");
            } else {
                finish(_wa, "Vertex index cannot be 0");
            }

            long long reported_sum; 
            if(!(cin >> reported_sum)) finish(_wa, "Unexpected EOF reading sum");

            if (is_removed[u]) finish(_wa, "Vertex already removed");

            
            if (in_mvc[u]) {
                
                
                in_mvc[u] = false;
                is_removed[u] = true;
                current_matching_size--;
                
                
            } else {
                
                
                
                
                
                int swap_v = -1;
                for (int v : g_adj[u]) {
                    if (!is_removed[v] && in_mvc[v]) {
                        if (D[v] == 1) {
                            swap_v = v;
                            break;
                        }
                    }
                }

                if (swap_v == -1) {
                    finish(_wa, "Removing this vertex does not reduce max matching size");
                }

                
                
                for (int z : g_adj[u]) {
                    if (!is_removed[z] && in_mvc[z]) {
                        D[z]--;
                    }
                }

                
                for (int z : g_adj[swap_v]) {
                    if (!is_removed[z] && in_mvc[z]) {
                        D[z]++;
                    }
                }

                in_mvc[swap_v] = false; 
                is_removed[u] = true;   
                current_matching_size--;
            }

        } else {
            
            int size_claimed;
            if(!(cin >> size_claimed)) finish(_wa, "Unexpected EOF reading size");

            if (size_claimed != current_matching_size) {
                finish(_wa, "Incorrect max matching size reported");
            }

            vector<int> edges_indices(size_claimed);
            vector<bool> v_covered(n1 + n2 + 1, false);
            
            for(int i=0; i<size_claimed; ++i) {
                if(!(cin >> edges_indices[i])) finish(_wa, "Unexpected EOF reading edge indices");
                
                int eid = edges_indices[i];
                if(eid < 1 || eid > m) finish(_wa, "Invalid edge index");

                int eu = edges[eid].first;
                int ev = n1 + edges[eid].second;

                if(is_removed[eu] || is_removed[ev]) finish(_wa, "Matching uses removed vertex");
                if(v_covered[eu] || v_covered[ev]) finish(_wa, "Not a matching (vertex shared)");

                v_covered[eu] = true;
                v_covered[ev] = true;
            }
            
            
        }
    }

    finish(_ok, "Passed all queries");
    return 0;
}


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

using namespace std;


static long long queries_cnt = 0;
static long long query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << queries_cnt << 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 MAXN = 400005; 
const int INF = 1e9;

struct Edge {
    int u, v, id;
};
vector<Edge> edges;

struct FlowEdge {
    int to;
    int rev; 
    int cap;
    int flow;
    int id; 
};

vector<FlowEdge> adj[MAXN];
int level[MAXN];
int ptr[MAXN];
int S, T;
int n1, n2, m, q;

void add_flow_edge(int from, int to, int cap, int id = -1) {
    FlowEdge a = {to, (int)adj[to].size(), cap, 0, id};
    FlowEdge b = {from, (int)adj[from].size(), 0, 0, -1};
    adj[from].push_back(a);
    adj[to].push_back(b);
}

bool bfs() {
    fill(level, level + T + 1, -1);
    level[S] = 0;
    queue<int> q_bfs;
    q_bfs.push(S);
    while (!q_bfs.empty()) {
        int v = q_bfs.front();
        q_bfs.pop();
        for (const auto& edge : adj[v]) {
            if (edge.cap - edge.flow > 0 && level[edge.to] == -1) {
                level[edge.to] = level[v] + 1;
                q_bfs.push(edge.to);
            }
        }
    }
    return level[T] != -1;
}

int dfs(int v, int pushed) {
    if (pushed == 0) return 0;
    if (v == T) return pushed;
    for (int& cid = ptr[v]; cid < (int)adj[v].size(); ++cid) {
        auto& edge = adj[v][cid];
        int tr = edge.to;
        if (level[v] + 1 != level[tr] || edge.cap - edge.flow == 0) continue;
        int push = dfs(tr, min(pushed, edge.cap - edge.flow));
        if (push == 0) continue;
        edge.flow += push;
        adj[tr][edge.rev].flow -= push;
        return push;
    }
    return 0;
}

int dinic() {
    int flow = 0;
    while (bfs()) {
        fill(ptr, ptr + T + 1, 0);
        while (int pushed = dfs(S, INF)) {
            flow += pushed;
        }
    }
    return flow;
}


set<int> initial_MVC; 



int current_match[MAXN]; 




vector<pair<int, int>> adj_simple[MAXN]; 
set<int> removed_vertices;
long long last_type1_sum = -1;


void build_mvc_and_match() {
    
    
    fill(current_match, current_match + T + 1, 0);
    for (int u = 1; u <= n1; ++u) {
        for (const auto& e : adj[u]) {
            if (e.to > n1 && e.to <= n1 + n2 && e.flow == 1) {
                current_match[u] = e.to;
                current_match[e.to] = u;
                break;
            }
        }
    }

    
    
    
    
    
    vector<bool> reachable(T + 1, false);
    queue<int> q_bfs;
    
    
    for (int u = 1; u <= n1; ++u) {
        if (current_match[u] == 0) {
            reachable[u] = true;
            q_bfs.push(u);
        }
    }
    
    while(!q_bfs.empty()) {
        int u = q_bfs.front(); q_bfs.pop();
        
        for (const auto& e : adj[u]) {
            
            if (e.cap - e.flow > 0) {
                if (e.to >= 1 && e.to <= n1 + n2 && !reachable[e.to]) {
                    reachable[e.to] = true;
                    q_bfs.push(e.to);
                }
            }
        }
    }
    
    
    for (int u = 1; u <= n1; ++u) {
        if (!reachable[u]) initial_MVC.insert(u);
    }
    for (int v = n1 + 1; v <= n1 + n2; ++v) {
        if (reachable[v]) initial_MVC.insert(v);
    }
}



bool vis[MAXN];
bool find_aug_path_bfs(int start_node, bool start_is_left) {
    
    fill(vis, vis + n1 + n2 + 1, false);
    queue<int> q_aug;
    
    q_aug.push(start_node);
    vis[start_node] = true;
    
    while(!q_aug.empty()) {
        int u = q_aug.front(); q_aug.pop();
        
        
        for (auto& edge : adj_simple[u]) {
            int v = edge.first; 
            if (removed_vertices.count(v)) continue;
            
            
            int next_u = current_match[v];
            if (next_u == 0) return true;
            
            
            if (!vis[next_u]) {
                vis[next_u] = true;
                q_aug.push(next_u);
            }
        }
    }
    return false;
}

int main(int argc, char** argv) {
    
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    registerInteraction(argc, argv);
    atexit(log_metrics);

    
    n1 = inf.readInt();
    n2 = inf.readInt();
    m = inf.readInt();
    q = inf.readInt();

    
    for (int i = 1; i <= m; ++i) {
        int u = inf.readInt();
        int v = inf.readInt();
        edges.push_back({u, v, i});
        
        
        adj_simple[u].push_back({n1 + v, i});
        adj_simple[n1 + v].push_back({u, i});
        
        
        add_flow_edge(u, n1 + v, 1, i);
    }

    
    S = 0; T = n1 + n2 + 1;
    for (int i = 1; i <= n1; ++i) add_flow_edge(S, i, 1);
    for (int i = 1; i <= n2; ++i) add_flow_edge(n1 + i, T, 1);
    
    
    vector<int> query_types(q);
    for (int i = 0; i < q; ++i) {
        query_types[i] = inf.readInt();
    }
    query_limit = q;

    
    dinic();
    build_mvc_and_match();

    
    cout << n1 << " " << n2 << " " << m << " " << q << endl;
    for (const auto& e : edges) {
        cout << e.u << " " << e.v << endl;
    }
    cout.flush();

    
    for (int k = 0; k < q; ++k) {
        int type = query_types[k];
        cout << type << endl; 
        
        
        
        if (type == 1) {
            queries_cnt++;
            
            
            
            
            
            int cnt;
            if (!(cin >> cnt)) finish(_wa, "Unexpected EOF reading query 1 answer (count)");
            if (cnt != 1) finish(_wa, "Must remove exactly 1 vertex");
            
            int v_idx;
            if (!(cin >> v_idx)) finish(_wa, "Unexpected EOF reading query 1 answer (vertex)");
            
            int v_abs = abs(v_idx);
            int v_real = -1;
            if (v_idx > 0) {
                if (v_idx > n1) finish(_wa, "Vertex index out of range");
                v_real = v_idx;
            } else if (v_idx < 0) {
                if (v_abs > n2) finish(_wa, "Vertex index out of range");
                v_real = n1 + v_abs;
            } else {
                finish(_wa, "Vertex index cannot be 0");
            }
            
            if (removed_vertices.count(v_real)) finish(_wa, "Vertex already removed");
            
            long long s;
            if (!(cin >> s)) finish(_wa, "Unexpected EOF reading query 1 answer (sum)");
            last_type1_sum = s;
            
            
            
            
            if (current_match[v_real] != 0) {
                
                
                
                
                
                
                
                
                int partner = current_match[v_real];
                current_match[v_real] = 0;
                current_match[partner] = 0; 
                removed_vertices.insert(v_real);
            } else {
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                removed_vertices.insert(v_real);
                finish(_wa, "Removing vertex did not decrease max matching size (vertex was unmatched)");
            }

        } else if (type == 2) {
            
            
            
            int k_ans;
            if (!(cin >> k_ans)) finish(_wa, "Unexpected EOF reading query 2 answer");
            
            vector<int> edge_indices(k_ans);
            long long sum_indices = 0;
            set<int> used_nodes;
            
            for(int i=0; i<k_ans; ++i) {
                if (!(cin >> edge_indices[i])) finish(_wa, "Unexpected EOF reading edges");
                if (edge_indices[i] < 1 || edge_indices[i] > m) finish(_wa, "Edge index out of range");
                
                sum_indices += edge_indices[i];
                Edge& e = edges[edge_indices[i]-1];
                
                int u = e.u;
                int v = n1 + e.v;
                
                if (removed_vertices.count(u) || removed_vertices.count(v)) {
                    finish(_wa, "Edge incident to removed vertex");
                }
                if (used_nodes.count(u) || used_nodes.count(v)) {
                    finish(_wa, "Invalid matching (overlapping edges)");
                }
                used_nodes.insert(u);
                used_nodes.insert(v);
            }
            
            if (sum_indices != last_type1_sum) {
                finish(_wa, "Sum of edge indices mismatch with previous query 1");
            }
            
            
            int current_size = 0;
            for(int i=1; i<=n1; ++i) if(current_match[i] != 0) current_size++;
            
            if (k_ans != current_size) {
                
                finish(_wa, "Matching size not maximum");
            }
            
        } else {
            finish(_wa, "Internal error: unknown query type");
        }
    }
    
    finish(_ok, "Passed");
    return 0;
}


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");
    
    
    long long seed_val = 0;
    if (argc > 1) seed_val = atoll(argv[1]);

    int n1, n2, m;
    
    
    if (seed_val > 0 && seed_val <= 3) {
        
        if (seed_val == 1) {
            n1 = 1; n2 = 1; m = 1;
        } else if (seed_val == 2) {
            n1 = 2; n2 = 3; m = 6; 
        } else {
            n1 = 5; n2 = 5; m = 4; 
        }
    } else {
        
        n1 = rnd.next(100, 200000);
        n2 = rnd.next(100, 200000);
        long long max_m = min((long long)n1 * n2, 200000LL);
        
        
        if (rnd.next(5) == 0) m = (int)max_m;
        else m = rnd.next(1, (int)max_m);
    }

    
    
    
    
    int limit_K = min({n1, n2, m});
    int K = rnd.next(1, limit_K);
    
    
    if (mode == "adp" || rnd.next(3) == 0) {
        K = rnd.next(limit_K / 2 + (limit_K % 2), limit_K); 
        if (K == 0) K = 1;
    }

    vector<pair<int,int>> edges;
    set<pair<int,int>> edge_set;
    
    
    vector<int> p1(n1), p2(n2);
    iota(p1.begin(), p1.end(), 1);
    iota(p2.begin(), p2.end(), 1);
    shuffle(p1.begin(), p1.end());
    shuffle(p2.begin(), p2.end());
    
    
    for(int i=0; i<K; ++i) {
        edges.push_back({p1[i], p2[i]});
        edge_set.insert({p1[i], p2[i]});
    }
    
    
    while((int)edges.size() < m) {
        int u = rnd.next(1, n1);
        int v = rnd.next(1, n2);
        if (edge_set.find({u, v}) == edge_set.end()) {
            edge_set.insert({u, v});
            edges.push_back({u, v});
        }
    }
    
    shuffle(edges.begin(), edges.end());

    
    
    
    int num_t1 = rnd.next(1, K);
    
    
    
    int num_t2 = rnd.next(0, 3);
    
    
    
    
    vector<int> t1_indices(num_t1);
    iota(t1_indices.begin(), t1_indices.end(), 0);
    shuffle(t1_indices.begin(), t1_indices.end());
    
    vector<bool> follow(num_t1, false);
    for(int i=0; i<min(num_t1, num_t2); ++i) {
        follow[t1_indices[i]] = true;
    }
    
    vector<int> queries;
    for(int i=0; i<num_t1; ++i) {
        queries.push_back(1);
        if(follow[i]) queries.push_back(2);
    }
    
    
    println(n1, n2, m, (int)queries.size());
    for(const auto& e : edges) {
        println(e.first, e.second);
    }
    for(int q : queries) {
        println(q);
    }

    return 0;
}