Codeforces Round #578 Div. 2

A B C D E F
模拟 贪心 数学 前缀和 kmp dfs,数学
1000 1300 1500 1900 2000 2500

A. Hotelier

题意

给出长度为 n 的字符串,L 表示有一个顾客从左边开始找房间,R 表示有一个顾客从右边开始找房间, ‘0’~‘9’表示相应的房间的客人离开,最后输出每个房间的情况。

题解

暴力模拟

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cout << fixed << setprecision(10);

  int n;
  string s;
  cin >> n >> s;
  vi a(10, 0);
  for (char ch : s) {
    if (ch == 'L') {
      FOR(i, 0, 10) {
        if (a[i] == 0) {
          a[i] = 1;
          break;
        }
      }
    } else if (ch == 'R') {
      REP(i, 0, 10) {
        if (a[i] == 0) {
          a[i] = 1;
          break;
        }
      }
    } else {
      a[ch - '0'] = 0;
    }
  }
  for (int x : a) {
    cout << x;
  }
  cout << "\n";
  return 0;
}

B. Block Adventure

题意

n 列方块,每列有 $ hi $ 个方块。你有一个袋子初始有 m 个方块,容量无限,你初始在第 1 列。 你可以从当前所在列拿或放方块,如果$| h_i - h{i+1}| \leq k$,你可以到下一列,问你能否到第 n 列。

题解

贪心,每次在当前列取尽量多的块。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const int MAXN = 105;

int h[MAXN];

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cout << fixed << setprecision(10);

  MUL_CASE {
    int n, k;
    ll m;
    cin >> n >> m >> k;
    FOR(i, 0, n) { cin >> h[i]; }
    FOR(i, 0, n - 1) {
      if (h[i] >= h[i + 1] - k) {
        m += h[i] - max(h[i + 1] - k, 0);
      } else if (h[i] + m < h[i + 1] - k) {
        m = -1;
        break;
      } else {
        m -= h[i + 1] - k - h[i];
      }
    }
    if (m >= 0) {
      cout << "YES\n";
    } else {
      cout << "NO\n";
    }
  }

  return 0;
}

C. Round Corridor

题意

一个圆盘有两层,内层有 n 块,外层有 m 块,内外层之间无墙,层内有墙。 q 次询问,问两个区域能否到达。

题解

每$(n / (n, m), m / (n, m))$块可以相通。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cout << fixed << setprecision(10);

  ll n, m;
  int q;
  cin >> n >> m >> q;
  ll g = __gcd(n, m);

  auto block = [&](ll x, ll y) {
    if (x == 1) {
      return (y - 1) / (n / g);
    } else {
      return (y - 1) / (m / g);
    }
  };

  while (q--) {
    ll sx, sy, ex, ey;
    cin >> sx >> sy >> ex >> ey;
    if (block(sx, sy) == block(ex, ey)) {
      cout << "YES\n";
    } else {
      cout << "NO\n";
    }
  }

  return 0;
}

D. White Lines

题意

给出$n \times n$的黑白方阵,你可以将一个$k \times k$的矩阵变白。问最多可以有多少行/列全白。

题解

枚举$k \times k$的矩阵,其对应的答案为除开此矩阵的白行/列+矩阵有多少行(列)左右(上下)都是白色。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104

const int MAXN = 2e3 + 5;

char s[MAXN][MAXN];
int cols[MAXN], rows[MAXN];
bool l[MAXN][MAXN], r[MAXN][MAXN], u[MAXN][MAXN], d[MAXN][MAXN], mark[MAXN];
int sc[MAXN][MAXN], sr[MAXN][MAXN];

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cout << fixed << setprecision(10);

  int n, k;
  cin >> n >> k;
  FOR(i, 1, n + 1) { cin >> (s[i] + 1); }

  FOR(i, 1, n + 1) {
    int x = 1;
    FOR(j, 1, n + 1) {
      if (s[i][j] == 'B') {
        x = 0;
        break;
      }
    }
    rows[i] = rows[i - 1] + x;
  }
  FOR(i, 1, n + 1) {
    int x = 1;
    FOR(j, 1, n + 1) {
      if (s[j][i] == 'B') {
        x = 0;
        break;
      }
    }
    cols[i] = cols[i - 1] + x;
  }

  fill(mark + 1, mark + n + 1, 1);
  FOR(i, 1, n + 1) {
    FOR(j, 1, n + 1) {
      u[i][j] = mark[j];
      if (s[i][j] == 'B') {
        mark[j] = 0;
      }
    }
  }
  fill(mark + 1, mark + n + 1, 1);
  FOR(j, 1, n + 1) {
    FOR(i, 1, n + 1) {
      l[i][j] = mark[i];
      if (s[i][j] == 'B') {
        mark[i] = 0;
      }
    }
  }
  fill(mark + 1, mark + n + 1, 1);
  REP(i, 1, n + 1) {
    FOR(j, 1, n + 1) {
      d[i][j] = mark[j];
      if (s[i][j] == 'B') {
        mark[j] = 0;
      }
    }
  }
  fill(mark + 1, mark + n + 1, 1);
  REP(j, 1, n + 1) {
    FOR(i, 1, n + 1) {
      r[i][j] = mark[i];
      if (s[i][j] == 'B') {
        mark[i] = 0;
      }
    }
  }
  /*

  FOR(i, 1, n + 1) { debug("%d rows:%d cols:%d\n", i, rows[i], cols[i]); }

  FOR2(i, 1, n + 1, j, 1, n + 1) {
    debug("(%d,%d) l:%d r:%d u:%d d:%d\n", i, j, l[i][j], r[i][j], u[i][j],
          d[i][j]);
  }
  */
  FOR2(i, 1, n + 1, j, 1, n + 1) {
    if (j >= k) {
      sr[i][j] = sr[i - 1][j] + (l[i][j - k + 1] && r[i][j]);
    }
    if (i >= k) {
      sc[i][j] = sc[i][j - 1] + (u[i - k + 1][j] && d[i][j]);
    }
    // debug("(%d,%d) sr:%d sc:%d\n", i, j, sr[i][j], sc[i][j]);
  }

  int ans = 0;
  FOR2(i, k, n + 1, j, k, n + 1) {
    ans =
        max(ans, rows[i - k] + rows[n] - rows[i] + sr[i][j] - sr[i - k][j] +
                     cols[j - k] + cols[n] - cols[j] + sc[i][j] - sc[i][j - k]);
  }
  cout << ans << "\n";

  return 0;
}

E. Compress Words

题意

给出 n 个单词,将单词依次连接。 A 和 B 连接回会删除 B 中 suffix(A)和 prefix(B)的最长相同部分。输出最终字符串。

题解

通过 kmp 可以求出 LCSP。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void get_next(char *S, int *nxt, int n) {
  nxt[0] = -1;
  int j = -1;
  for (int i = 1; i < n; ++i) {
    while ((~j) && S[j + 1] != S[i]) {
      j = nxt[j];
    }
    nxt[i] = (S[j + 1] == S[i]) ? (++j) : j;
  }
}

int pattern(char *S, char *T, int *nxt, int n, int m) {
  int j = -1;
  for (int i = 0; i < m; ++i) {
    while ((~j) && S[j + 1] != T[i]) {
      j = nxt[j];
    }
    j += S[j + 1] == T[i];
  }
  return j;
}

const int MAXL = 1e6 + 5;
char s[MAXL], ans[MAXL];
int nxt[MAXL];

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cout << fixed << setprecision(10);

  int n;
  cin >> n;
  cin >> ans;
  int l2 = strlen(ans);
  FOR(i, 1, n) {
    cin >> s;
    int l1 = strlen(s);
    get_next(s, nxt, l1);
    int pl = min(l1, l2);
    int j = pattern(s, ans + l2 - pl, nxt, l1, pl);
    FOR(k, j + 1, l1) { ans[l2++] = s[k]; }
  }
  cout << ans << "\n";
  return 0;
}

F. Graph Traveler

题意

n 个点,每个点有$ k_i $和 $ m_i $出边。 每次旅行有初值 c,每到达点 v,c+=v,之后沿$ e[v][c mod m_i] $继续。 q 次询问,问从 x 出发,初值为 y,问有多少个点可以无限被访问。

题解

因为$ m_i < 10 $,所以可以将所有的权值对 2250 取模,所有状态数变为 2250n,可以 dfs 算出答案。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
const int MAXN = 1e3;
const int LCM = 2520;

int k[MAXN], e[10], ans[MAXN][LCM];
pii nxt[MAXN][LCM], mark[MAXN][LCM];

void dfs(int x, int y, const pii &st) {
  mark[x][y] = st;
  int _x = nxt[x][y].first;
  int _y = nxt[x][y].second;

  if (mark[_x][_y] == pii(-1, -1)) {
    dfs(_x, _y, st);
    ans[x][y] = ans[_x][_y];
  } else {
    if (mark[_x][_y] == st) {
      int cx = x;
      int cy = y;
      static set<int> s;
      s.clear();
      do {
        s.insert(cx);
        _x = nxt[cx][cy].first;
        _y = nxt[cx][cy].second;
        cx = _x;
        cy = _y;
      } while (cx != x || cy != y);
      ans[x][y] = s.size();
    } else {
      ans[x][y] = ans[_x][_y];
    }
  }
}

int main() {

  // freopen("input.txt", "r", stdin);

  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cout << fixed << setprecision(10);

  int n;
  cin >> n;
  FOR(i, 0, n) {
    cin >> k[i];
    k[i] %= LCM;
    if (k[i] < 0) {
      k[i] += LCM;
    }
  }
  FOR(i, 0, n) {
    int m;
    cin >> m;
    FOR(j, 0, m) {
      cin >> e[j];
      --e[j];
    }
    FOR(j, 0, LCM) {
      int c = (k[i] + j) % LCM;
      int v = e[c % m];
      nxt[i][j] = {v, c};
    }
  }

  memset(ans, -1, sizeof(ans));
  memset(mark, -1, sizeof(mark));
  FOR(i, 0, n) {
    FOR(j, 0, LCM) {
      if (mark[i][j] != pii(-1, -1)) {
        continue;
      }
      dfs(i, j, {i, j});
    }
  }

  int q;
  cin >> q;
  while (q--) {
    int x, y;
    cin >> x >> y;
    cout << ans[x - 1][(y % LCM + LCM) % LCM] << "\n";
  }

  return 0;
}
comments powered by Disqus