Thunder8971's blog

By Thunder8971, 2 hours ago, In English

Hello, Codeforces!

It's been a while since I last seriously participated in competitive programming. I've thought about coming back many times, but today I want to share something a little different from an algorithm or a contest experience.

I've always loved competing. Not just winning, but facing difficult problems, challenging myself, and discovering how far I could go. Whether it was competitive programming, research, or simply learning something new, I loved pursuing things that seemed almost impossible.

But sometimes life changes in ways we could never anticipate.

In 2025, I was diagnosed with retinitis pigmentosa, a progressive retinal disease. My night vision and peripheral vision were already severely compromised, and I was told I might have around five years of useful sight left.

I was terrified. Suddenly, everything seemed pointless. I knew I could still learn and accomplish things without sight, but I couldn't imagine losing the life I knew. It felt as though everything I dreamed of had been overshadowed by something I couldn't stop.

The strange thing was that my academic and professional performance didn't decline. I continued studying, working, and fulfilling my responsibilities just as I always had. But the things I used to do simply because I loved them were different. Competitive programming, playing piano or violin, and even sharing what I knew with others no longer brought me the happiness they once had. Little by little, I stopped doing many of those things.

From the outside, very little seemed to have changed. Inside, I felt almost nothing. When someone reminded me of my past achievements, I knew they were talking about me, yet those memories felt as though they belonged to someone else. I couldn't connect the person I remembered with the person living my life. Sometimes I cried; other times I felt completely empty, even when I wasn't alone.

I didn't want to become someone different. I just wanted to feel like myself again.

My family stood by me throughout that time, even when I couldn't explain what I was feeling. I also received encouragement from people I'll always be grateful for. I tried returning to things that once made me happy, and at one point, almost instinctively, I helped someone who needed to study. But even then, I couldn't feel the connection I was searching for.

Then, during the first week of May 2026, something unexpected happened.

I was alone in my room when I noticed a blue hardcover book on my shelf. I picked it up and realized it was something I had written when I was fifteen, filled with my dreams, experiences, and thoughts about pursuing what matters to us. I'd written it hoping to inspire others to follow their dreams with everything they had.

I never imagined that, years later, I would be the one who needed those words.

As I read, I remembered not only what I wanted to accomplish, but why. For the first time in what felt like an eternity, I felt connected to myself again. I was happy because I could finally recognize myself, and sad because I couldn't believe how long I'd spent feeling like a stranger in my own life.

The book I'd written to inspire others ended up inspiring me.

A few days later, something happened that I still find almost unbelievable. Someone I thought I would never hear from again suddenly contacted me. She had once been one of my closest friends, but we'd lost contact two years earlier, and I hadn't known what had happened to her.

We had shared competitions, research, dreams, and ideals. When we started talking again, she reminded me of experiences we'd had together, including moments I'd completely forgotten. Hearing those stories brought back memories that had felt lost to me for so long.

She also wanted to help me return to the things I used to love. She even had ideas for activities we could enjoy together, just as we had shared so many interests before. We talked about the life we'd once dreamed of, filled with learning, curiosity, competitions, and seemingly impossible goals. Despite the difficulties she'd experienced, she still believed in those dreams.

To her, she was simply being herself, reconnecting with an old friend. To me, those conversations and her willingness to share those experiences again meant more than she could imagine.

Little by little, I returned to swimming, piano, violin, and astronomy. I took out my old telescope and, for the first time, saw Saturn's rings. I can't describe how wonderful that felt. My family, friends, that book, and my own efforts had all played a part. I wasn't trying to return to the past. I simply wanted to feel like myself again and enjoy the life I was still living.

Recently, I visited my retinal specialist again. He explained that my disease was already advanced and had an unusual presentation: despite my severely compromised visual field and night vision, the characteristic retinal pigmentation wasn't clearly visible. I couldn't help finding a little irony in having retinitis pigmentosa without visible pigment.

But his message was serious. He told me that I could lose my remaining vision soon, although no one could predict exactly when. He encouraged me to enjoy the vision I still have and reminded me that, ultimately, only God had the final say.

Those words were difficult to hear. I'd be lying if I said I wasn't afraid. But this time, something was different. I don't want that fear to take away the life I still have. There are so many things I want to learn, discover, create, and share with others. I don't know what the future will bring, but I know I want to keep moving forward, searching for solutions and pursuing my dreams, even when the path becomes difficult.

I may not be able to choose what happens to my vision, but I don't want to lose myself to that uncertainty again. I want to keep doing what I love, appreciating the people around me, and finding reasons to smile along the way.

Later, while practicing violin and thinking about everything I'd been through, I felt something I hadn't felt in a long time: nostalgia and pride. Pride in being able to enjoy the things I love again, and in finally feeling like myself.

So why share this story here? Because behind every Codeforces handle, there's a person with a life we cannot fully see. Someone might stop competing or disappear for months. Someone else might keep performing well while quietly struggling. We can't know what someone is going through just by looking at their rating or achievements.

Maybe someone reading this has been feeling lost, has stopped enjoying something they once loved, or is going through something so difficult that they can't imagine things getting better. If that's you, I want you to know that you're not alone.

I know that hearing someone tell you to be strong doesn't magically make things better. There were times when I couldn't even recognize myself, much less imagine feeling happy again. But things can change, sometimes in ways we never expect. It might take time, support, or something that reminds you of what matters to you. You don't have to figure everything out today, and asking for help doesn't mean you've given up.

Somewhere in this community, there's someone who once felt completely lost too, and who eventually found his way back. If you ever feel like talking, you're welcome to send me a message. I may not have all the answers, but I'd be happy to listen when I can.

As for me, I still intend to compete, study, do research, and search for ways to contribute to a solution for my disease. Some attempts may fail, and I may need help or a different approach, but I have no intention of giving up on that search.

And I still carry the dream I wrote about at fifteen: helping build a world where more people have the chance to pursue their own dreams.

Thank you for reading my story. From the bottom of my heart, I hope that whatever you're facing, you find the support, hope, and opportunities to keep pursuing what matters to you. Even when the road ahead seems uncertain, there may still be beautiful things waiting to be discovered.

See you in the next contest. <3

Full text and comments »

  • Vote: I like it
  • +8
  • Vote: I do not like it

By Thunder8971, 18 months ago, In English

Building Segment Trees Iteratively: A Step-by-Step Guide

This blog aims to explain a simple iterative segment tree that is easy to memorize due to its step-by-step approach while remaining efficient. This segment tree is designed for competitive programming contests that do not allow templates but still require this technique to be implemented quickly.

Introduction

The segment tree is a data structure that enables range queries with updates. In this blog, I will demonstrate how to construct an iterative segment tree with this approach to solve the following operations:

  1. Query the minimum value in a range.
  2. Add a given value to each element in a range.

Step 1: Preparations

To begin, we need to adjust the size of the given array. For instance, if the array is named $$$A$$$ and has a size $$$N$$$, we must find the smallest power of $$$2$$$ greater than or equal to $$$N$$$. This adjustment is possible because the additional values we append to $$$A$$$ will never be accessed. To achieve this, we first implement the function $$$\text{getPower(N)}$$$.

// Function to find the smallest power of 2 greater than or equal to n
int getPower(int n) {
    int l = 0, r = 26; // Define the binary search range for powers of 2
    while (l < r) {
        int mid = (l + r) / 2; // Calculate the midpoint of the range
        if ((1 << mid) >= n) { // Check if 2^mid is greater than or equal to n
            r = mid; // Narrow the search range to the left
        } else {
            l = mid + 1; // Narrow the search range to the right
        }
    }
    return l; // Return the smallest power of 2
}

At this point, $$$N$$$ is now a power of $$$2$$$, so it is essential to keep this detail in mind moving forward.

Step 2: Creating the Segment Tree

As we know, a Segment Tree is a binary tree that, when the original array (of size $$$N$$$) has a size that is a power of $$$2$$$, uses $$$2N$$$ nodes. Since $$$N$$$ is a power of $$$2$$$, the indices corresponding to $$$A$$$ in the Segment Tree will be $$$i+n-1$$$. The construction takes advantage of the fact that if we have a node with index $$$x$$$, then its parent is $$$\frac{x}{2}$$$. To build the Segment Tree, we first assign the values of the array to the leaves, and then for each node, we combine the results of its children to form each node. The children of each node are $$$2*x$$$ and $$$2x+1$$$.

vector<int> st(2 * n + 1), lz(2 * n + 1), A(n + 1);
// Function to build the segment tree
void build() {
    // Assign values from the array to the leaves of the segment tree
    for (int i = 1; i <= n; i++) {
        A[i] = st[i + n - 1]; // Map array elements to the corresponding leaf nodes
    }
    // Combine child nodes to form parent nodes
    for (int i = n - 1; i >= 1; i--) {
        st[i] = min(st[2 * i], st[2 * i + 1]); // Compute the minimum of the left and right children
    }
}

With this, the segment tree is ready to start performing queries and modifications.

Step 3: Creating the Query Function

To solve the problems, we need to view everything from the leaves to the root. Suppose we want a query in the interval $$$[a,b]$$$. Since we know that $$$N$$$ is a power of $$$2$$$, the node for $$$a$$$ will be $$$a+N - 1$$$ and similarly for $$$b$$$. However, this is not enough to solve the problem, as we need to filter which nodes are the main ones for our interval and which are not. To do this, we define $$$L$$$ and $$$R$$$ as the left and right limits, respectively. If the left limit is at a left node, then that node is not a definitive node in the interval because, as $$$R$$$ is the right limit, the node $$$L+1$$$ is also part of the interval, or if $$$L=R$$$, then either $$$L$$$ is on the right and necessary, or $$$R$$$ is on the left and necessary. The same applies to $$$R$$$, but in reverse.

// Function to query the minimum value in a range [a, b]
int query(int a, int b) {
    int l = a + n - 1, r = b + n - 1; // Convert range to segment tree indices
    bool reg = false; // Flag to check if the result is initialized
    int ans = 0; // Variable to store the result
    while (l <= r) {
        if (l % 2 == 1) { // If l is a right child
            propagateUpToX(l); // Propagate updates to the current node
            ans = reg ? min(ans, st[l]) : st[l]; // Update the result with the minimum value
            reg = true; // Mark the result as initialized
            l++; // Move to the next node
        }
        if (r % 2 == 0) { // If r is a left child
            propagateUpToX(r); // Propagate updates to the current node
            ans = reg ? min(ans, st[r]) : st[r]; // Update the result with the minimum value
            reg = true; // Mark the result as initialized
            r--; // Move to the previous node
        }
        l /= 2; // Move to the parent node
        r /= 2; // Move to the parent node
    }
    return ans; // Return the minimum value in the range
}

At this point, we have a function that solves the minimum query, so now let's see how to add range updates with lazy propagation.

Step 4: Adding Lazy Propagation

Now that we have finished the first point, we need to start with the next point. To address this problem, we will use the lazy propagation technique, which involves placing an update tag and updating only when necessary. Usually, this technique is implemented from the root node to the leaves (note that this is the reverse of our iterative segment tree), but we can take advantage of something to make the same traversal, and that is the binary representation of numbers, from the highest power to the lowest. If that power is present, we move to the right; otherwise, we move to the left. As we progress through the nodes to be updated, the update tags are propagated. The main update function goes down to the leaves, captures the main nodes, and calls the rest of the functions we have defined.

// Function to propagate lazy updates to a node
void propagate(int x) {
    st[x] += lz[x]; // Apply the lazy value to the current node
    if (x < n) { // If the node is not a leaf
        lz[2 * x] += lz[x]; // Propagate the lazy value to the left child
        lz[2 * x + 1] += lz[x]; // Propagate the lazy value to the right child
    }
    lz[x] = 0; // Clear the lazy value for the current node
}
// Function to propagate updates from the root to a specific node x
void propagateUpToX(int x) {
    int cur = x, h = 0; // Start from the target node and calculate its height
    while (cur > 0) {
        h++; // Increment the height
        cur /= 2; // Move to the parent node
    }
    cur = 1; // Start from the root
    for (int i = h - 2; i >= 0; i--) {
        propagate(cur); // Propagate updates to the current node
        cur = (x & (1 << i)) ? 2 * cur + 1 : 2 * cur; // Move to the left or right child based on the bit
    }
    propagate(cur); // Propagate updates to the target node
}
// Function to update the segment tree up to a specific node x
void updateUpToX(int x) {
    int cur = x; // Start from the target node
    propagate(cur); // Propagate updates to the current node
    cur /= 2; // Move to the parent node
    while (cur > 0) {
        propagate(cur); // Propagate updates to the current node
        propagate(2 * cur); // Propagate updates to the left child
        propagate(2 * cur + 1); // Propagate updates to the right child
        st[cur] = min(st[2 * cur], st[2 * cur + 1]); // Update the current node with the minimum value of its children
        cur /= 2; // Move to the parent node
    }
}
// Function to update a range [a, b] with a value v
void update(int a, int b, int v) {
    int l = a + n - 1, r = b + n - 1; // Convert range to segment tree indices
    while (l <= r) {
        if (l % 2 == 1) { // If l is a right child
            propagateUpToX(l); // Propagate updates to the current node
            lz[l] += v; // Add the value to the lazy array
            updateUpToX(l); // Update the segment tree up to the current node
            l++; // Move to the next node
        }
        if (r % 2 == 0) { // If r is a left child
            propagateUpToX(r); // Propagate updates to the current node
            lz[r] += v; // Add the value to the lazy array
            updateUpToX(r); // Update the segment tree up to the current node
            r--; // Move to the previous node
        }
        l /= 2; // Move to the parent node
        r /= 2; // Move to the parent node
    }
}

With this, we have range updates, but one thing is missing. The query section also requires updating the nodes up to the nodes we need, and for that, we just need to add a few lines to the code.

// Function to query the minimum value in a range [a, b]
int query(int a, int b) {
    int l = a + n - 1, r = b + n - 1; // Convert range to segment tree indices
    bool reg = false; // Flag to check if the result is initialized
    int ans = 0; // Variable to store the result
    while (l <= r) {
        if (l % 2 == 1) { // If l is a right child
            propagateUpToX(l); // Propagate updates to the current node
            ans = reg ? min(ans, st[l]) : st[l]; // Update the result with the minimum value
            reg = true; // Mark the result as initialized
            l++; // Move to the next node
        }
        if (r % 2 == 0) { // If r is a left child
            propagateUpToX(r); // Propagate updates to the current node
            ans = reg ? min(ans, st[r]) : st[r]; // Update the result with the minimum value
            reg = true; // Mark the result as initialized
            r--; // Move to the previous node
        }
        l /= 2; // Move to the parent node
        r /= 2; // Move to the parent node
    }
    return ans; // Return the minimum value in the range
}

All the code together would be:

// Organized and cleaned-up code for better readability

// Global variables
vector<int> st(2 * n + 1), lz(2 * n + 1), A(n + 1);

// Function to find the smallest power of 2 greater than or equal to n
int getPower(int n) {
    int l = 0, r = 26; // Define the binary search range for powers of 2
    while (l < r) {
        int mid = (l + r) / 2; // Calculate the midpoint of the range
        if ((1 << mid) >= n) { // Check if 2^mid is greater than or equal to n
            r = mid; // Narrow the search range to the left
        } else {
            l = mid + 1; // Narrow the search range to the right
        }
    }
    return l; // Return the smallest power of 2
}

// Function to build the segment tree
void build() {
    // Assign values from the array to the leaves of the segment tree
    for (int i = 1; i <= n; i++) {
        A[i] = st[i + n - 1]; // Map array elements to the corresponding leaf nodes
    }
    // Combine child nodes to form parent nodes
    for (int i = n - 1; i >= 1; i--) {
        st[i] = min(st[2 * i], st[2 * i + 1]); // Compute the minimum of the left and right children
    }
}

// Function to propagate lazy updates to a node
void propagate(int x) {
    st[x] += lz[x]; // Apply the lazy value to the current node
    if (x < n) { // If the node is not a leaf
        lz[2 * x] += lz[x]; // Propagate the lazy value to the left child
        lz[2 * x + 1] += lz[x]; // Propagate the lazy value to the right child
    }
    lz[x] = 0; // Clear the lazy value for the current node
}

// Function to propagate updates from the root to a specific node x
void propagateUpToX(int x) {
    int cur = x, h = 0; // Start from the target node and calculate its height
    while (cur > 0) {
        h++; // Increment the height
        cur /= 2; // Move to the parent node
    }
    cur = 1; // Start from the root
    for (int i = h - 2; i >= 0; i--) {
        propagate(cur); // Propagate updates to the current node
        cur = (x & (1 << i)) ? 2 * cur + 1 : 2 * cur; // Move to the left or right child based on the bit
    }
    propagate(cur); // Propagate updates to the target node
}

// Function to update the segment tree up to a specific node x
void updateUpToX(int x) {
    int cur = x; // Start from the target node
    propagate(cur); // Propagate updates to the current node
    cur /= 2; // Move to the parent node
    while (cur > 0) {
        propagate(cur); // Propagate updates to the current node
        propagate(2 * cur); // Propagate updates to the left child
        propagate(2 * cur + 1); // Propagate updates to the right child
        st[cur] = min(st[2 * cur], st[2 * cur + 1]); // Update the current node with the minimum value of its children
        cur /= 2; // Move to the parent node
    }
}

// Function to update a range [a, b] with a value v
void update(int a, int b, int v) {
    int l = a + n - 1, r = b + n - 1; // Convert range to segment tree indices
    while (l <= r) {
        if (l % 2 == 1) { // If l is a right child
            propagateUpToX(l); // Propagate updates to the current node
            lz[l] += v; // Add the value to the lazy array
            updateUpToX(l); // Update the segment tree up to the current node
            l++; // Move to the next node
        }
        if (r % 2 == 0) { // If r is a left child
            propagateUpToX(r); // Propagate updates to the current node
            lz[r] += v; // Add the value to the lazy array
            updateUpToX(r); // Update the segment tree up to the current node
            r--; // Move to the previous node
        }
        l /= 2; // Move to the parent node
        r /= 2; // Move to the parent node
    }
}

// Function to query the minimum value in a range [a, b]
int query(int a, int b) {
    int l = a + n - 1, r = b + n - 1; // Convert range to segment tree indices
    bool reg = false; // Flag to check if the result is initialized
    int ans = 0; // Variable to store the result
    while (l <= r) {
        if (l % 2 == 1) { // If l is a right child
            propagateUpToX(l); // Propagate updates to the current node
            ans = reg ? min(ans, st[l]) : st[l]; // Update the result with the minimum value
            reg = true; // Mark the result as initialized
            l++; // Move to the next node
        }
        if (r % 2 == 0) { // If r is a left child
            propagateUpToX(r); // Propagate updates to the current node
            ans = reg ? min(ans, st[r]) : st[r]; // Update the result with the minimum value
            reg = true; // Mark the result as initialized
            r--; // Move to the previous node
        }
        l /= 2; // Move to the parent node
        r /= 2; // Move to the parent node
    }
    return ans; // Return the minimum value in the range
}

Conclusion

In this blog, we explored the implementation of an iterative segment tree, a powerful data structure for solving range queries and updates efficiently. We started by understanding the basics of segment trees, followed by step-by-step explanations of building the tree, querying ranges, and implementing lazy propagation for range updates. The iterative approach presented here is designed to be simple, efficient, and easy to implement in competitive programming scenarios.

Thank you for taking the time to read this blog. I hope it has been helpful in deepening your understanding of segment trees. If you have any questions, feedback, or suggestions, feel free to share them. Happy coding!!!

Full text and comments »

  • Vote: I like it
  • +10
  • Vote: I do not like it

By Thunder8971, history, 22 months ago, In English

After today's contest, jiangly reached a rating of 3977, just 23 points below 4000. Could jiangly be the next to reach a rating of 4000?

Full text and comments »

  • Vote: I like it
  • +73
  • Vote: I do not like it