Tuesday, November 25, 2014

Conveyor Belts (Codeforces Round 278 Div. 1 Problem D)

Problem Statement:
Codeforces 278 Div. 1
D. Conveyor Belts

Solution:
What a challenging problem, I could never imagine solving this kind of problem during a real contest. Cool people, cool stuff, cool problem. Let's keep learning!

The problem is actually a range minimum query (RMQ) problem and hence solvable using any data structure that can efficiently compute such queries (\(O(N\lg{N})\) segment tree, \(O(\sqrt{N})\)  square root decomposition, and the like). How can we model this as a RMQ problem?



Firstly notice that the conveyor belt only goes UP, it never goes back down again. This suggest that if we have a range of rows [L, R] (L denote the upper row, R denote the lower row, I use L and R since it's more natural notation for segment tree hehe), then the bread will leave this rows at some point, or never at all, but it will never leave from R. This means that if a bread is to enter [L, R], it must enter from R. Now if we can compute the exiting point of a bread that enter [L, R] at (R, i) (where i is the column index), we can simulate the conveyor belt in a more efficient way. Furthermore, if we already know the exiting position of all bread starting from (M, i) in [L, M], and exiting position of all bread starting at (R, i) in [M+1, R], we can find the exiting position of all bread starting at (R, i) in [L, R] in O(m) (which can be taken as O(1)).

From here you "just" need to implement a segment tree using this idea. Here I provide my implementation, which I think can be way more precise/shorter/cleverer, but that's the best I can do for now :)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <vector>
#include <utility>
using namespace std;

pair<int,int> segtree[400100][13];
char b[100400][19];
int N,M,Q;

void compute(int p, int row){
    for(int i=0;i<=M+1;++i){
        segtree[p][i] = make_pair(row, 12);
    }
    for(int i=1;i<=M;++i){
        if(b[row][i-1] == '<'){
            segtree[p][i] = make_pair(row, 0);
        } else break;
    }
    for(int i=M;i>=1;--i){
        if(b[row][i-1] == '>'){
            segtree[p][i] = make_pair(row, M+1);
        } else break;
    }
    for(int i=1;i<=M;++i){
        if(b[row][i-1] == '^'){
            segtree[p][i] = make_pair(row-1, i);
            for(int j=i-1;j>=1;--j){
                if(b[row][j-1] == '>'){
                    segtree[p][j] = make_pair(row-1, i);
                } else break;
            }
            for(int j=i+1;j<=M;++j){
                if(b[row][j-1] == '<'){
                    segtree[p][j] = make_pair(row-1, i);
                } else break;
            }
        }
    }
    /*
    printf("row: %d\n", row);
    for(int i=1;i<=M;++i){
        printf("(%d,%d) ", segtree[p][i].first, segtree[p][i].second);
    }
    printf("\n");
    */
}

void combine(int p, int L, int R){
    int mid = (L+R)/2;
    for (int i=1;i<=M;++i){
        pair<int,int> P = segtree[2*p+1][i];
        if(mid+1 <= P.first){
            segtree[p][i] = P;
        } else {
            segtree[p][i] = segtree[2*p][P.second];
        }
    }
}

void build(int p, int L, int R){
    if(L==R){
        compute(p, L);
        return;
    }
    int mid = (L+R)/2;
    build(2*p, L, mid);
    build(2*p+1, mid+1, R);
    combine(p, L, R);
}

void update(int p, int S, int L, int R){
    if(R < S || S < L)return;
    if(S == L && L == R) {
        compute(p, S);
        return;
    }
    int mid = (L+R)/2;
    update(2*p, S, L, mid);
    update(2*p+1, S, mid+1, R);
    combine(p, L, R);
}

pair<int, int> query(int p, pair<int,int> q, int L, int R){
    //printf("q=(%d,%d) [%d,%d]\n", q.first, q.second, L, R);
    if(q.first == L && R == L){
        return segtree[p][q.second];
    }
    int mid = (L+R)/2;
    if(q.first <= mid){
        return query(2*p, q, L, mid);
    } else {
        pair<int,int> tmp = query(2*p+1, q, mid+1, R);
        if(tmp.second == 12 || mid+1 <= tmp.first) return tmp;
        return segtree[2*p][tmp.second];
    }
}

int main(){
    scanf("%d %d %d", &N, &M, &Q);
    for(int i=0;i<N;++i){
        scanf("%s",b[i]);
    }
    build(1, 0, N-1);
    char ch, ty;
    int u, v;
    for(int qq=0;qq<Q;++qq){
        scanf(" %c %d %d", &ty, &u, &v);
        if(ty == 'A'){
            pair<int,int> ret = query(1, make_pair(u-1,v), 0, N-1);
            if(ret.second == 12) printf("-1 -1\n");
            else printf("%d %d\n", ret.first+1, ret.second);
        } else {
            scanf(" %c", &ch);
            b[u-1][v-1] = ch;
            update(1, u-1, 0, N-1);
        }
    }
    return 0;
}

2 comments:

  1. I appreciate your cogged v belt post. This is incredibly helpful to us.

    ReplyDelete
  2. This problem is a classic example of range minimum query (RMQ), which is a common technique used to efficiently compute queries over ranges, such as finding the minimum element within a specific subarray. The challenge here is to simulate the behavior of a conveyor belt that only moves up, and to calculate the exit points of bread on the conveyor belt as it moves through different rows.

    The key observation is that once we know the exit point of bread starting from a given position, we can efficiently simulate its journey along the belt. By breaking the problem into smaller subproblems—computing the exit points for different ranges of rows—we can leverage efficient data structures like segment trees, which allow for fast querying and updating.

    Using a segment tree to store and manage these exit points will help reduce the time complexity, making it feasible to solve this problem in competitive programming contests where time is a critical factor.


    pharmeasy franchise
    Rack Supported Mezzanine floor

    ReplyDelete