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
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_set>
using namespace std;

struct Pos {
    int x, y;
    
    bool operator==(const Pos& other) const {
        return x == other.x && y == other.y;
    }
};

// Custom hash function for Pos
struct PosHash {
    size_t operator()(const Pos& pos) const {
        return hash<int>()(pos.x) ^ (hash<int>()(pos.y) << 1);
    }
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m, k, q;
    cin >> n >> m >> k >> q;
    
    // Use unordered_set for efficient lookup, insertion, and deletion
    unordered_set<Pos, PosHash> blocks;
    
    // Read initial blocks
    for (int i = 0; i < k; i++) {
        int x, y;
        cin >> x >> y;
        blocks.insert({x, y});
    }
    
    // Function to check if a block can be removed
    auto can_remove = [&](const Pos& pos, const unordered_set<Pos, PosHash>& current_blocks) -> bool {
        // Check if we can grab left and right sides
        bool left_free = current_blocks.find({pos.x, pos.y - 1}) == current_blocks.end();
        bool right_free = current_blocks.find({pos.x, pos.y + 1}) == current_blocks.end();
        
        // Check if we can grab top and bottom sides
        bool top_free = current_blocks.find({pos.x - 1, pos.y}) == current_blocks.end();
        bool bottom_free = current_blocks.find({pos.x + 1, pos.y}) == current_blocks.end();
        
        return (left_free && right_free) || (top_free && bottom_free);
    };
    
    // Function to calculate maximum removable blocks
    auto calculate_max_removable = [&]() -> int {
        // Copy the blocks for simulation
        unordered_set<Pos, PosHash> temp_blocks = blocks;
        int count = 0;
        
        bool made_progress = true;
        while (made_progress && !temp_blocks.empty()) {
            made_progress = false;
            
            // Find blocks that can be removed
            vector<Pos> to_remove;
            for (const auto& pos : temp_blocks) {
                if (can_remove(pos, temp_blocks)) {
                    to_remove.push_back(pos);
                }
            }
            
            // Remove the blocks
            for (const auto& pos : to_remove) {
                temp_blocks.erase(pos);
                count++;
                made_progress = true;
            }
        }
        
        return count;
    };
    
    // Calculate for initial configuration
    vector<int> results;
    results.push_back(calculate_max_removable());
    
    // Process each move
    for (int i = 0; i < q; i++) {
        int x, y;
        cin >> x >> y;
        
        Pos pos = {x, y};
        auto it = blocks.find(pos);
        
        if (it == blocks.end()) {
            // Add a block
            blocks.insert(pos);
        } else {
            // Remove a block
            blocks.erase(it);
        }
        
        // Calculate for the new configuration
        results.push_back(calculate_max_removable());
    }
    
    // Output results
    for (int result : results) {
        cout << result << "\n";
    }
    
    return 0;
}