Pigeonhole Principle

Aman NadafAman Nadaf
6 min read

Hello Readers,

The pigeonhole principle is quite straightforward but it can be used under various circumstances. It is also called Dirichlet's box principle or Dirichlet's drawer principle. If you have nine pigeonholes and nine pigeons. It is possible to keep each pigeon in a separate pigeonhole. But as soon as there is one more pigeon, one of the pigeonholes has to end up with more than one pigeon. You can't keep ten pigeons in nine pigeonholes without making at least one pigeonhole to keep more than one pigeon.

In general, form, if you have n pigeonholes and m (m > n) pigeons then you can't keep all the pigeons without making at least one pigeonhole to fill with more than one pigeonhole.

Sock Picking Problem

Now, we will use this principle as proof for some very common problems. One of the well-known problems is the sock-picking problem. If you have pair of red, blue, and black socks in a drawer. Without looking inside a drawer how many socks you should pick to ensure at least one pair of socks?

If we pick 1 sock:

It could be of any one color: red, black, blue

If we pick 2 socks:

You could get: red-black, red-blue, black-blue

if we pick 3 socks:

You could get: red-black-blue

But, if pick 4 socks it is guaranteed that there is at least one pair exists. It can be red, blue or black.

In the above example, we can use the pigeonhole analogy to derive the answer much more easily. Suppose each color is a pigeonhole and each sock is a pigeon, now to make sure you have at least one pigeonhole with more than one pigeon, you need at least one more pigeon than the number of pigeonholes.

Birthday Problem

How many students do you need in a class to make sure that at least two students have the same birthday?

Let's again use the pigeonhole principle to solve this problem.

You have 365 days in a year (not considering leap year for now).

So, in order to have at least two students with the same birthday we should have a minimum of 366 students.

Is divisible by N?

If you are given an array with N + 1 positive numbers. Is it possible that the sum of some subset is divisible by N?

Let's say the given array is as follows:

$$\begin{align} & A = [a_1 \space a_2 \space a_3 \space .... \space a_n \space a_{n+1}] \\ \end{align}$$

Now let us calculate the prefix array for the array A.

$$\begin{align} & p_i = \Sigma_{i=1}^{i} a_i \\ & P = [p_1 \space p_2 \space p_3 \space .... \space p_n \space p_{n+1}] \\ \end{align}$$

Now take store remainders of the element when divided by N.

$$\begin{align} & R = [ r_1 \space r_2 \space r_3 \space .... \space r_n \space r_{n+1} ]\\ \end{align}$$

If you divide any number by N, the remainder can only be in the range 0 to N - 1, inclusive.

So, by the pigeonhole principle in the above array R, at least one value is repeated.

If I assume those indexes to be l and r. The sum from P[l + 1] to P[r] is divisible by N.

Why?

If two numbers N and M (N < M), have the same remainder when divided by K, (N - M) is always divisible by K.

C8KBF and Tree

The problem requires you to find four nodes a, b, c, and d. Such that:

  1. a < b and c < d

  2. The value of a to b is equal to the value of c to d.

The value of the simple path from x to y is defined as the bitwise XOR of the weights of the edges it consists of.

We will also take a look at the constraint as it plays a very important role in solving this problem.

$$\begin{align} & 1 \le T \le 10 \\ & 1 \le W_i \le 2^{20} \\ & 3 \le N \le 10^{5} \end{align}$$

As you can see all the weights are less than 2 to the power of 20, so the bitwise XOR of any two elements can not exceed (2 to the power of 21 - 1).

$$\begin{align} & value(x, y) \le 2^{21} - 1 \end{align}$$

This value is approximately equal to 20 to the power of 5. So the value of the path of any two nodes can not exceed this limit. In other words, we can try all the paths till we exceed this limit and when we will do exceed this limit we will have two paths with the exact same value.

So, firstly we will calculate all the values of all the paths from 1 to all the other nodes. then from 2 to all other nodes which are greater than 2 i.e. 3, 4, 5, etc.

$$\begin{align} & \text{As in a single test case we will only do } 20^5 operations \\ & \text{And there can at max be 10 test cases} \\ & \text{The overall operations become } 10 \times 20^{5} = 20^{6} \\ & \text{which is less than the given time limit of} 10^8 operations. \\ \end{align}$$

$$NOTE: \text{In 1 second we can do around } 10^8 operations.$$

We will use DFS to count the value between each path. Then we will use the map to store all the current values of nodes.

#include<bits/stdc++.h>
using namespace std;
#define int long long 

vector<vector<pair<int, int>>> graph;
int startNode;
bool dfs(int src, int par, map<int, vector<pair<int, int>>> &values, vector<int> &distance) {
    for(auto [node, weight]: graph[src]) {
        /*
            We don't need visited array as there can be only one node which                    
            is previous visited i.e. parent node.
        */
        if(node == par) {
            continue;
        }
        /*
            Updating the xor distance of node with respected to its path.
        */
        distance[node] = distance[src] ^ weight;

        /*
            We only need to check when node is greater than starting node.
        */
        if(node > startNode) {
            int curDistance = distance[node];
            pair<int, int> curPair = make_pair(startNode, node);
            /*
                If the value already exist then we simply need to return         
                true.
            */
            if(values.count(curDistance)) {
                values[curDistance].push_back(curPair);
                return true;
            } 
            values[curDistance].push_back(curPair);
        }

        bool isTrue = dfs(node, src,  values, distance);
        if(isTrue) {
            return true;
        }
    }
    return false;
}

void solve() {
    int n;
    cin >> n;
    graph = vector<vector<pair<int, int>>> (n + 1);
    /*
        For a tree number of edges is one less than number of vertices.
    */
    for(int i = 0; i < n - 1; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        graph[u].push_back(make_pair(v, w));
        graph[v].push_back(make_pair(u, w));
    }

    map<int, vector<pair<int, int>>> values;
    /*
        This map stores all the pair with particular xor value.
    */
    bool flag = false;
    for(int i = 1; i <= n; i++) {
        startNode = i;
        vector<int> distance(n + 1);
        distance[startNode] = 0;
        /*
            At i'th index, distance stores the xor distance from starting   
            node to i.
        */
        if(dfs(i, -1, values, distance)) {
            flag = true;
            break;
        }
    }

    if(flag) {
        pair<int, int> AtoB, CtoD;
        for(auto itr: values) {
            int sz = (int) itr.second.size();
            if(sz >= 2) {
                AtoB = itr.second[0];
                CtoD = itr.second[1];
                break;
            }
        }

        cout << AtoB.first << " " << AtoB.second << " ";
        cout << CtoD.first << " " << CtoD.second << endl;
    } else {
        cout << -1 << endl;
    }
}

int32_t main() {
    int test;
    cin >> test;
    while(test--) {
        solve();
    }
    return 0;
}

If you have any doubts or feedback please share it below.

Thank You!

TeckBakers\iRatherFear

2
Subscribe to my newsletter

Read articles from Aman Nadaf directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Aman Nadaf
Aman Nadaf

I am a competitive programmer, here to write blogs & share knowledge.