E - Rotate and Flip Editorial by lucifer1004


It is natural to think of sorting the queries first, according to their timestamps.

Now let’s look at the operations. Suppose we have \((x,y)\):

  • Operation I changes it to \((y,-x)\)
  • Operation II changes it to \((-y, x)\)
  • Operation III changes it to \((2p-x,y)\)
  • Operation IV changes it to \((x,2p-y)\)

Since the operations are applied to all points at the same time, we will not calculate the position of each point, instead, we want to know the final transform.

Operation I & II swaps \(x\) and \(y\), while changing the sign of one axis. Operation III & IV flips one axis and then adds \(2p\) to it. To handle all the operations, we will use five variables, \(swap\) denoting if \(x\) and \(y\) are swapped, \(s_x\) denoting the sign of the current \(x\) axis, \(s_y\) denoting the sign of the current \(y\) axis, \(c_x\) denoting the extra value for \(x\) and \(c_y\) denoting the extra value for \(y\).

Now we have:

  • Operation I: \(swap\) is flipped, \(c_x\) and \(c_y\) is swapped, \(s_x\) and \(s_y\) is swapped, then \(s_y\) and \(c_y\) are flipped.
  • Operation II: \(swap\) is flipped, \(c_x\) and \(c_y\) is swapped, \(s_x\) and \(s_y\) is swapped, then \(s_x\) and \(c_x\) are flipped.
  • Operation III: \(s_x\) is flipped, and \(c_x\) is flipped then added \(2p\).
  • Operation IV: \(s_y\) is flipped, and \(c_y\) is flipped then added \(2p\).

For each query, after applying enough operations, we can answer the query according to the five variables:

  • If \(swap\) is false, the answer will be \((s_xx_0+c_x,s_yy_0+c_y)\)

  • If \(swap\) is true, the answer will be \((s_xy_0+c_x,s_yx_0+c_y)\)

  • Time complexity is \(\mathcal{O}(Q\log Q)\).

  • Space complexity is \(\mathcal{O}(Q)\).

Source code:

use proconio::input;
use proconio::marker::Usize1;
use std::cmp::Ordering;

fn main() {
    input! {
        n: usize,
        points: [(i64, i64); n],
        m: usize,
    }

    let mut ops: Vec<(usize, i64)> = vec![];
    for _i in 0..m {
        input! {
            t: usize,
        }
        if t <= 2 {
            ops.push((t, 0));
        } else {
            input! {
                p: i64,
            }
            ops.push((t, p));
        }
    }

    input! {
        q: usize,
        queries: [(usize, Usize1); q],
    }

    let mut ans = vec![(0i64, 0i64); q];
    let mut order: Vec<usize> = (0..q).collect();
    order.sort_by(|a, b| if queries[*a].0 <= queries[*b].0 {
        Ordering::Less
    } else {
        Ordering::Greater
    });

    let mut swap = false;
    let mut sx = 1i64;
    let mut sy = 1i64;
    let mut cx = 0i64;
    let mut cy = 0i64;
    let mut op = 0usize;
    for i in order {
        let (a, b) = queries[i];
        while op < a {
            let (t, p) = ops[op];
            match t {
                1 => {
                    swap = !swap;
                    std::mem::swap(&mut cx, &mut cy);
                    std::mem::swap(&mut sx, &mut sy);
                    sy = -sy;
                    cy = -cy;
                },
                2 => {
                    swap = !swap;
                    std::mem::swap(&mut cx, &mut cy);
                    std::mem::swap(&mut sx, &mut sy);
                    sx = -sx;
                    cx = -cx;
                },
                3 => {
                    sx = -sx;
                    cx = p * 2 - cx;
                },
                4 => {
                    sy = -sy;
                    cy = p * 2 - cy;
                },
                _ => {

                }
            }
            op += 1;
        }
        if swap {
            ans[i] = (points[b].1 * sx + cx, points[b].0 * sy + cy);
        } else {
            ans[i] = (points[b].0 * sx + cx, points[b].1 * sy + cy);
        }
    }

    for i in 0..q {
        println!("{} {}", ans[i].0, ans[i].1);
    }
}

posted:
last update: