zoukankan      html  css  js  c++  java
  • 九校联考-长沙市一中NOIP模拟Day1T2 跳房子(jump)

    问题描述

    跳房子,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
    跳房子是在N个格子上进行的,CYJ对游戏进行了改进,该成了跳棋盘,改进后的游戏是在一个N行M列的棋盘上进行,并规定从第一行往上可以走到最后一行,第一列往左可以走到最后一列,反之亦然。每个格子上有一个数字。
    在这个棋盘左上角(1,1)放置着一枚棋子。每次棋子会走到右、右上和右下三个方向格子中对应上数字最大一个。即任意时刻棋子都只有一种走法,不存在多个格子同时满足条件。
    现在有两种操作:
    move k 将棋子前进k步。
    change a b e 将第a行第b列格子上的数字修改为e。
    请对于每一个move操作输出棋子移动完毕后所处的位置。

    输入

    第一行包含两个正整数N,M(3<=N,M<=2000),表示棋盘的大小。
    接下来N行,每行M个整数,依次表示每个格子中的数字a[i,j](1<= a[i,j]<=109)。
    接下来一行包含一个正整数Q(1<=Q<=5000),表示操作次数。
    接下来m行,每行一个操作,其中1<=a<=N,1<=b<=M,1<=k,e<=109。

    输出

    对于每个move操作,输出一行两个正整数x,y,即棋子所处的行号和列号。

    输入输出样例

    样例输入

    4 4
    1 2 9 3
    3 5 4 8
    4 3 2 7
    5 8 1 6
    4
    move 1
    move 1
    change 1 4 100
    move 1

    样例输出

    4 2
    1 3
    1 4

    数据范围

    10%的数据满足:3<= N,M <=50,Q<=5000, k<=10;
    20%的数据满足:3<= N,M <=200,Q<=5000,k<=5000;
    另有20%的数据满足:3<= N,M <=200,Q<=5000,k<=109;
    100%的数据满足:3<= N,M <=2000,Q<=5000,e,k<=109;

    记录第一列第i行能走到位置jump[i],询问时先一行一行的走,每次走到jump[i]行第一列,
    不足一行时一步一步走,修改时暴力重构jump数组

    代码

    #include <cstdio>
    #include <limits>
    
    #define MAX 2000
    
    int R, C;
    int a[MAX][MAX];
    int pos_r = 0, pos_c = 0;
    int jump[MAX];
    
    int Fix(int x, int X) {
      return (x % X + X) % X;
    }
    
    int NextRow(int r, int c) {
      int next_row = -1;
      int max_value = -1;
      for (int rr = r - 1; rr <= r + 1; ++rr) {
        const int value = a[Fix(rr, R)][Fix(c + 1, C)];
        if (value > max_value) {
          max_value = value;
          next_row = rr;
        }
      }
      return next_row;
    }
    
    int RunToFirstColumn(int r, int c) {
      do {
        r = Fix(NextRow(r, c++), R);
      } while (c < C);
      return r;
    }
    
    struct Interval {
      Interval() = default;
      Interval(int lo, int hi) : lo(lo), hi(hi) {}
      bool IsEmpty() const { return lo > hi; }
      int Size() const { return hi - lo + 1; }
      bool Contains(int x) const { return lo <= x && x <= hi; }
      int lo = std::numeric_limits<int>::max();
      int hi = std::numeric_limits<int>::min();
    };
    
    void Update(int r, int c) {
      const int target_row = RunToFirstColumn(r, c);
      Interval rows(r, r);
      int cc = c;
      while (cc > 0) {
        --cc;
        Interval new_rows; 
        for (int rr = rows.lo - 1; rr <= rows.lo + 1; ++rr) {
          if (rows.Contains(NextRow(rr, cc))) {
            new_rows.lo = rr;
            break;
          }
        }
        for (int rr = rows.hi + 1; rr >= rows.hi - 1; --rr) {
          if (rows.Contains(NextRow(rr, cc))) {
            new_rows.hi = rr;
            break;
          }
        }
        if (new_rows.IsEmpty()) return;
        rows = new_rows;
      }
      if (rows.Size() >= R) {
        for (int rr = 0; rr < R; ++rr) {
          jump[rr] = target_row;
        }
      } else {
        for (int rr = rows.lo; rr <= rows.hi; ++rr) {
          jump[Fix(rr, R)] = target_row;
        }
      }
      return;
    }
    
    void SuperWalk(int k) {
      static int walk_id = 0;
      static int last_walk_id[MAX];
      static int last_k[MAX];
      ++walk_id;
      
      if (k >= C - pos_c) {
        k -= C - pos_c;
        pos_r = RunToFirstColumn(pos_r, pos_c);
        pos_c = 0;
      }
      while (k >= C) {
        k -= C;
        pos_r = jump[pos_r];
    
        if (last_walk_id[pos_r] == walk_id) {
          const int cycle_len = last_k[pos_r] - k;
          if (k >= cycle_len) {
            const int n_cycles = k / cycle_len;
            k -= n_cycles * cycle_len;
          }
        }
        last_walk_id[pos_r] = walk_id;
        last_k[pos_r] = k;
      }
      while (k--) {
        pos_r = Fix(NextRow(pos_r, pos_c++), R);
      }
    }
    
    int main() {
      scanf("%d%d", &R, &C);
      for (int r = 0; r < R; ++r) {
        for (int c = 0; c < C; ++c) {
          int k;
          scanf("%d", &k);
          a[r][c] = k;
        }
      }
      for (int r = 0; r < R; ++r) {
        jump[r] = RunToFirstColumn(r, 0);
      }
    
      int Q;
      scanf("%d", &Q);
      for (int qq = 0; qq < Q; ++qq) {
        static char command[16];
        scanf("%s", command);
        if (command[0] == 'm') {
          int k;
          scanf("%d", &k);
          SuperWalk(k);
          printf("%d %d
    ", pos_r + 1, pos_c + 1);
        } else {
          int r, c, k;
          scanf("%d%d%d", &r, &c, &k); --r; --c;
          a[r][c] = k;
          for (int rr = r - 1; rr <= r + 1; ++rr) {
            Update(Fix(rr, R), Fix(c - 1, C));
          }
        }
      }
      return 0;
    }
    
  • 相关阅读:
    贫血,充血模型的解释以及一些经验(非常经典)(非原创)
    源代码管理安装大全
    20条常见的编码陷阱 你中枪了没?(转)
    从30岁到35岁:为你的生命多积累一些厚度(转)
    Model1 与Model2(转)
    白话MVP(转帖)
    stl string 使用
    TerminateThread不要使用的證據
    C++静态成员函数小结(转)
    C/C++必知必会1
  • 原文地址:https://www.cnblogs.com/shulker/p/9656427.html
Copyright © 2011-2022 走看看