CodeForces Gym 101669 简要题解

Concerts

f(i,j) f ( i , j ) 表示最后一次匹配的位置 i ≤ i ,匹配了 j j 个位置的方案数。

#include 

using namespace std;

const int N = 100005;
const int M = 305;
const int mod = 1e9 + 7;

int n, m, wait[M], f[N][M];
char s[N], t[M];

int add(int x, int y) {
  x += y;
  if (x >= mod) {
    x -= mod;
  }
  return x;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d %d", &m, &n);
  for (int i = 0; i < 26; ++i) {
    scanf("%d", &wait[i]);
  }
  scanf("%s%s", t + 1, s + 1);
  f[0][0] = 1;
  for (int i = 1; i <= n; ++i) {
    for (int j = 0; j <= m; ++j) {
      f[i][j] = f[i - 1][j];
      if (s[i] == t[j]) {
        if (j == 1) {
          f[i][j] = add(f[i][j], f[i - 1][j - 1]);
        } else {
          int k = i - wait[t[j - 1] - 'A'] - 1;
          if (k >= 0) {
            f[i][j] = add(f[i][j], f[k][j - 1]);
          }
        }
      }
    }
  }
  printf("%d\n", f[n][m]);
  return 0;
}

Bricks

不难发现砖块掉落的顺序不影响最后答案,枚举空位进行DP转移,那么就是要区间长度和区间内砖块个数相等。

#include 

using namespace std;

const int N = 1100005;
const int mod = 1e9 + 7;

int n, m, f[N], g[N], s[N];

int add(int x, int y) {
  x += y;
  if (x >= mod) {
    x -= mod;
  }
  return x;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d %d", &n, &m);
  ++n;
  while (m--) {
    int x;
    scanf("%d", &x);
    ++s[x];
  }
  for (int i = 1; i <= n; ++i) {
    s[i] += s[i - 1];
  }
  g[n] = 1;
  for (int i = 1; i <= n; ++i) {
    if (s[i] == s[i - 1]) {
      f[i] = g[s[i - 1] - (i - 1) + n];
      g[s[i] - i + n] = add(g[s[i] - i + n], f[i]);
    }
  }
  printf("%d\n", f[n]);
  return 0;
}

Christmas Tree

对每种颜色求出它的极长链,那么相当于要给颜色定一个拓扑序,用倍增优化连边即可。

#include 

using namespace std;

const int N = 100005;
const int M = 17;
const int S = 2000005;

int n, m, total, color[N], depth[N], answer_x[N], answer_y[N], degree[S], id[M][N], anc[M][N];
vector<int> adj[N], nodes[N], to[S];

void dfs(int x) {
  for (int i = 1; i < M; ++i) {
    anc[i][x] = anc[i - 1][anc[i - 1][x]];
  }
  for (auto y : adj[x]) {
    if (y != anc[0][x]) {
      anc[0][y] = x;
      depth[y] = depth[x] + 1;
      dfs(y);
    }
  }
}

int jump(int x, int d) {
  for (int i = 0; i < M; ++i) {
    if (d >> i & 1) {
      x = anc[i][x];
    }
  }
  return x;
}

int lca(int x, int y) {
  if (depth[x] > depth[y]) {
    x = jump(x, depth[x] - depth[y]);
  } else if (depth[x] < depth[y]) {
    y = jump(y, depth[y] - depth[x]);
  }
  if (x == y) {
    return x;
  }
  for (int i = M - 1; ~i; --i) {
    if (anc[i][x] != anc[i][y]) {
      x = anc[i][x];
      y = anc[i][y];
    }
  }
  return anc[0][x];
}

int dist(int x, int y) {
  return depth[x] + depth[y] - (depth[lca(x, y)] << 1);
}

int find(int x, int y, int z, int d) {
  if (d <= depth[x] - depth[z]) {
    return jump(x, d);
  } else {
    return jump(y, depth[x] + depth[y] - (depth[z] << 1) - d);
  }
}

void add_edge(int color, int x, int y) {
  if (depth[x] < depth[y]) {
    swap(x, y);
  }
  if (depth[x] > depth[y]) {
    for (int i = 0; i < M; ++i) {
      if (depth[x] - depth[y] >> i & 1) {
        to[color].push_back(id[i][x]);
        x = anc[i][x];
      }
    }
  }
  if (x == y) {
    to[color].push_back(id[0][x]);
    return;
  }
  for (int i = M - 1; ~i; --i) {
    if (anc[i][x] != anc[i][y]) {
      to[color].push_back(id[i][x]);
      to[color].push_back(id[i][y]);
      x = anc[i][x];
      y = anc[i][y];
    }
  }
  to[color].push_back(id[0][x]);
  to[color].push_back(id[0][y]);
  to[color].push_back(id[0][anc[0][x]]);
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d %d", &n, &m);
  for (int i = 1; i <= n; ++i) {
    scanf("%d", &color[i]);
    nodes[color[i]].push_back(i);
  }
  for (int i = 1; i < n; ++i) {
    int x, y;
    scanf("%d %d", &x, &y);
    adj[x].push_back(y);
    adj[y].push_back(x);
  }
  dfs(1);
  total = m;
  for (int i = 0; i < M; ++i) {
    for (int j = 1; j <= n; ++j) {
      id[i][j] = ++total;
    }
  }
  for (int i = 1; i <= n; ++i) {
    to[id[0][i]].push_back(color[i]);
  }
  for (int i = 1; i < M; ++i) {
    for (int j = 1; j <= n; ++j) {
      if (anc[i - 1][j]) {
        to[id[i][j]].push_back(id[i - 1][j]);
        to[id[i][j]].push_back(id[i - 1][anc[i - 1][j]]);
      }
    }
  }
  for (int i = 1; i <= m; ++i) {
    if (nodes[i].empty()) {
      printf("%d 1 1\n", i);
      continue;
    }
    int x = nodes[i][0], y = nodes[i][0], diameter = 0;
    for (int j = 1; j < nodes[i].size(); ++j) {
      int z = nodes[i][j], d, type = 0;
      d = dist(x, z);
      if (d > diameter) {
        diameter = d;
        type = 1;
      }
      d = dist(y, z);
      if (d > diameter) {
        diameter = d;
        type = 2;
      }
      if (type == 1) {
        y = z;
      } else if (type == 2) {
        x = z;
      }
    }
    answer_x[i] = x;
    answer_y[i] = y;
    int z = lca(x, y);
    vectorint, int>> all;
    for (auto p : nodes[i]) {
      all.push_back(make_pair(dist(x, p), p));
    }
    sort(all.begin(), all.end());
    for (int j = 1; j < all.size(); ++j) {
      if (all[j].first != all[j - 1].first + 1) {
        int u = find(x, y, z, all[j].first - 1), v = find(x, y, z, all[j - 1].first + 1);
        add_edge(i, u, v);
      }
    }
  }
  for (int x = 1; x <= total; ++x) {
    for (auto y : to[x]) {
      ++degree[y];
    }
  }
  queue<int> q;
  for (int i = 1; i <= total; ++i) {
    if (!degree[i]) {
      q.push(i);
    }
  }
  while (!q.empty()) {
    int x = q.front();
    q.pop();
    if (x <= m && !nodes[x].empty()) {
      printf("%d %d %d\n", x, answer_x[x], answer_y[x]);
    }
    for (auto y : to[x]) {
      if (!--degree[y]) {
        q.push(y);
      }
    }
  }
  return 0;
}

Harry Potter and The Vector Spell

将每个向量的两个 1 1 连一条边,对于一个连通块,它的贡献是它的大小减 1 1

#include 

using namespace std;

const int N = 100005;

int n, m, f[N], size[N];
vector<int> a[N];

int find(int x) {
  while (x != f[x]) {
    x = f[x] = f[f[x]];
  }
  return x;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d %d", &m, &n);
  for (int i = 1; i <= m; ++i) {
    int k;
    scanf("%d", &k);
    while (k--) {
      int x;
      scanf("%d", &x);
      a[x].push_back(i);
    }
    f[i] = i;
  }
  for (int i = 1; i <= n; ++i) {
    f[find(a[i][0])] = find(a[i][1]);
  }
  for (int i = 1; i <= m; ++i) {
    ++size[find(i)];
  }
  int answer = 0;
  for (int i = 1; i <= m; ++i) {
    if (size[i]) {
      answer += size[i] - 1;
    }
  }
  printf("%d\n", answer);
  return 0;
}

Looping Playlist

预处理每种状态是否合法,枚举开头结尾接起来的那一段是哪种调,然后贪心即可。

#include 

using namespace std;

const int N = 10000005;

map<string, int> f;
int n, a[N];
bool ok[N];

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(0);
  cin.tie(0);
  f["Do"] = 0;
  f["Do#"] = 1;
  f["Re"] = 2;
  f["Re#"] = 3;
  f["Mi"] = 4;
  f["Fa"] = 5;
  f["Fa#"] = 6;
  f["Sol"] = 7;
  f["Sol#"] = 8;
  f["La"] = 9;
  f["La#"] = 10;
  f["Si"] = 11;
  for (int i = 0; i < 12; ++i) {
    int s = 0;
    s |= 1 << (i + 0) % 12;
    s |= 1 << (i + 2) % 12;
    s |= 1 << (i + 4) % 12;
    s |= 1 << (i + 5) % 12;
    s |= 1 << (i + 7) % 12;
    s |= 1 << (i + 9) % 12;
    s |= 1 << (i + 11) % 12;
    ok[s] = true;
  }
  for (int i = 1; i < 1 << 12; i <<= 1) {
    for (int j = 0; j < 1 << 12; j += i << 1) {
      for (int k = 0; k < i; ++k) {
        ok[j + k] |= ok[j + k + i];
      }
    }
  }
  cin >> n;
  for (int i = 1; i <= n; ++i) {
    string s;
    cin >> s;
    a[i] = f[s];
  }
  int answer = n;
  for (int i = 0; i < 12; ++i) {
    int s = 0;
    s |= 1 << (i + 0) % 12;
    s |= 1 << (i + 2) % 12;
    s |= 1 << (i + 4) % 12;
    s |= 1 << (i + 5) % 12;
    s |= 1 << (i + 7) % 12;
    s |= 1 << (i + 9) % 12;
    s |= 1 << (i + 11) % 12;
    int l = 1, r = n;
    while (l <= n && (s >> a[l] & 1)) {
      ++l;
    }
    while (r && (s >> a[r] & 1)) {
      --r;
    }
    int result = 1, state = (1 << 12) - 1;
    for (int j = l; j <= r; ++j) {
      if (!ok[state | 1 << a[j]]) {
        state = 1 << a[j];
        ++result;
      } else {
        state |= 1 << a[j];
      }
    }
    answer = min(answer, result);
  }
  printf("%d\n", answer);
  return 0;
}

Binary Transformations

肯定是先把按照 c c 从大到小的顺序把 1 1 变成 0 0 ,再按照从小到大的顺序把 0 0 变成 1 1 。注意一开始可以将一些 1 1 先变成 0 0 再变成 1 1 ,枚举变了前多少大的计算即可。

#include 

using namespace std;

typedef long long ll;

const int N = 5005;

pair<int, int> all[N];
char s[N], t[N];
int n, a[N];
bool use[N];

ll solve() {
  ll result = 0, sum = 0;
  for (int i = 1; i <= n; ++i) {
    if (s[i] == '1') {
      sum += a[i];
    }
  }
  for (int i = n; i; --i) {
    int x = all[i].second;
    if (use[x] || (s[x] == '1' && t[x] == '0')) {
      sum -= a[x];
      result += sum;
    }
  }
  for (int i = 1; i <= n; ++i) {
    int x = all[i].second;
    if (use[x] || (s[x] == '0' && t[x] == '1')) {
      sum += a[x];
      result += sum;
    }
  }
  return result;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i) {
    scanf("%d", &a[i]);
    all[i] = make_pair(a[i], i);
  }
  sort(all + 1, all + n + 1);
  scanf("%s %s", s + 1, t + 1);
  vectorint, int>> to_change;
  for (int i = 1; i <= n; ++i) {
    if (s[i] == '1' && t[i] == '1') {
      to_change.push_back(make_pair(-a[i], i));
    }
  }
  sort(to_change.begin(), to_change.end());
  ll answer = solve();
  for (auto p : to_change) {
    use[p.second] = true;
    answer = min(answer, solve());
  }
  printf("%lld\n", answer);
  return 0;
}

Robots

模拟。

#include 

using namespace std;

typedef long long ll;

ll solve(vector> a) {
  ll v = 0, result = 0;
  for (auto p : a) {
    result += 2ll * v * p.second + p.first * p.second * p.second;
    v += p.first * p.second;
  }
  return result;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%lld %lld", &a[i].first, &a[i].second);
  }
  ll answer = -solve(a);
  sort(a.begin(), a.end());
  reverse(a.begin(), a.end());
  answer += solve(a);
  printf("%lld.%d\n", answer >> 1, answer & 1 ? 5 : 0);
  return 0;
}

Cat and Mouse

考虑如果猫和老鼠在相邻的边上了,老鼠先走,那么猫有两种策略:

  • 顺着怼过去。

  • 老鼠走一步,猫停留,老鼠走回来(必须回来不然不优),猫和老鼠走到一个点上,老鼠走一步,猫跟上,老鼠走一步,猫不动(相当于用 4 4 步的代价堵住老鼠要往复的边,老鼠位置不变)。

将每条边 (mouse,cat) ( m o u s e , c a t ) 作为状态预处理到终止态的最短路,然后枚举老鼠走了几步之后猫开始堵住老鼠,可以通过预处理每个点的深度来知道猫是否能到达,之后就用预处理好的最短路来计算答案。

#include 

using namespace std;

const int N = 100005;
const int inf = 0x3f3f3f3f;

int n, mouse, dist[N], depth[N];
vectorint, int>> edges, to[N];
vector<int> adj[N], pool[N << 2];

void dfs(int x, int parent) {
  for (auto y : adj[x]) {
    if (y != parent) {
      depth[y] = depth[x] + 1;
      dfs(y, x);
    }
  }
}

int get_id(int x, int y) {
  return lower_bound(edges.begin(), edges.end(), make_pair(x, y)) - edges.begin();
}

int find_next(int x, int ban) {
  for (auto y : adj[x]) {
    if (y != ban) {
      return y;
    }
  }
  return -1;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int T;
  scanf("%d", &T);
  while (T--) {
    scanf("%d %d", &n, &mouse);
    --mouse;
    for (int i = 0; i < n; ++i) {
      adj[i].clear();
    }
    edges.clear();
    for (int i = 0; i < n - 1; ++i) {
      int x, y;
      scanf("%d %d", &x, &y);
      --x;
      --y;
      adj[x].push_back(y);
      adj[y].push_back(x);
      edges.push_back(make_pair(x, y));
      edges.push_back(make_pair(y, x));
    }
    sort(edges.begin(), edges.end());
    for (int i = 0; i < n; ++i) {
      reverse(adj[i].begin(), adj[i].end());
    }
    if (!~find_next(mouse, -1)) {
      puts("0");
      continue;
    }
    dfs(0, -1);
    for (int i = 0; i < n << 2; ++i) {
      pool[i].clear();
    }
    for (int i = 0; i < edges.size(); ++i) {
      dist[i] = inf;
      to[i].clear();
    }
    for (int i = 0; i < edges.size(); ++i) {
      int x = edges[i].first, y = edges[i].second, z = find_next(x, y);
      if (!~z) {
        dist[i] = 0;
        pool[0].push_back(i);
      } else {
        to[get_id(z, x)].push_back(make_pair(i, 1));
        if (find_next(z, -1) == x) {
          to[get_id(x, z)].push_back(make_pair(i, 4));
        }
      }
    }
    for (int i = 0; i < n << 2; ++i) {
      for (auto x : pool[i]) {
        if (dist[x] == i) {
          for (auto e : to[x]) {
            if (dist[e.first] > i + e.second) {
              dist[e.first] = i + e.second;
              pool[i + e.second].push_back(e.first);
            }
          }
        }
      }
    }
    int answer = inf, current = mouse;
    for (int i = 0; i < n; ++i) {
      int next = find_next(current, i ? -1 : 0);
      if (depth[next] < i || (depth[next] == i && depth[current] != i - 1)) {
        answer = min(answer, i + dist[get_id(current, next)]);
      }
      current = next;
    }
    printf("%d\n", answer);
  }
  return 0;
}

Tetris

状态不多,记忆化搜索,碰到栈中的元素答案就是 1 − 1

#include 

using namespace std;

const int N = 55;
const char s[3][N] = {
"010111110011110010010010011001",
"111100011010010010111011111111",
"010100001110011111011110110110"
};

map<int, int> mem[N], visit[N];
int n, a[N];

int go(int state, int type, int rotate) {
  if (state >> 21) {
    return -1;
  }
  char b[3][3];
  if (!rotate) {
    for (int i = 0; i < 3; ++i) {
      for (int j = 0; j < 3; ++j) {
        b[i][j] = s[i][j + type * 3];
      }
    }
  } else if (rotate == 1) {
    for (int i = 0; i < 3; ++i) {
      for (int j = 0; j < 3; ++j) {
        b[i][j] = s[j][2 - i + type * 3];
      }
    }
  } else if (rotate == 2) {
    for (int i = 0; i < 3; ++i) {
      for (int j = 0; j < 3; ++j) {
        b[i][j] = s[2 - i][2 - j + type * 3];
      }
    }
  } else {
    for (int i = 0; i < 3; ++i) {
      for (int j = 0; j < 3; ++j) {
        b[i][j] = s[2 - j][i + type * 3];
      }
    }
  }
  int p = 7;
  for (int i = 6; ~i; --i) {
    bool flag = false;
    for (int j = 0; j < 3; ++j) {
      for (int k = 0; k < 3; ++k) {
        if (b[j][k] == '1' && (state >> (i + j) * 3 + k & 1)) {
          flag = true;
        }
      }
    }
    if (flag) {
      break;
    }
    p = i;
  }
  int new_state = state;
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      if (b[i][j] == '1') {
        new_state |= 1 << (i + p) * 3 + j;
      }
    }
  }
  for (int i = 0; i < 10; ++i) {
    while ((new_state >> i * 3 & 7) == 7) {
      new_state = (new_state & (1 << 3 * i) - 1) | (new_state >> (i + 1) * 3 << i * 3);
    }
  }
  return new_state;
}

int dfs(int state, int x) {
  if (visit[x][state] == 1) {
    return -1;
  }
  if (visit[x][state] == 2) {
    return mem[x][state];
  }
  visit[x][state] = 1;
  int result = 0;
  for (int i = 0; i < 4; ++i) {
    int next = go(state, a[x], i);
    if (~next) {
      int value = dfs(next, (x + 1) % n);
      if (!~value) {
        return -1;
      }
      result = max(result, value + 1);
    }
  }
  visit[x][state] = 2;
  mem[x][state] = result;
  return result;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d", &n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
  }
  printf("%d\n", dfs(0, 0));
  return 0;
}

Cunning Friends

打表找规律。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, one = 0, two = 0, three = 0;
  scanf("%d", &n);
  if (n == 1) {
    puts("Win");
    return 0;
  }
  while (n--) {
    int x;
    scanf("%d", &x);
    if (x == 1) {
      ++one;
    } else if (x == 2) {
      ++two;
    } else {
      ++three;
    }
  }
  if (three > 1 || two > 2 || three + two == 3) {
    puts("Lose");
    return 0;
  }
  if (three + two == 1 || one % 3) {
    puts("Win");
  } else {
    puts("Lose");
  }
  return 0;
}

Escape Room

考虑 Ai=1 A i = 1 的,显然我们会顺着放 N,N1,N2, N , N − 1 , N − 2 , ⋯ 然后开始放 Ai=2 A i = 2 的。排序即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vectorint, int>> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i].first);
    a[i].second = i;
  }
  sort(a.begin(), a.end(), greaterint, int>> ());
  vector<int> answer(n);
  for (int i = 0; i < n; ++i) {
    answer[a[i].second] = i;
  }
  for (int i = 0; i < n; ++i) {
    printf("%d%c", answer[i] + 1, i == n - 1 ? '\n' : ' ');
  }
  return 0;
}

Divide and Conquer

因为总边数是 4(n1) 4 ( n − 1 ) ,所以一定有一个点度数不超过 3 3 ,所以答案不超过 3 3 。所以有一棵树一定只断了一条边,那么在另一棵树中,如果一条边两端连接的两个点在第一棵树中是不同的连通块,那么这条边就要割掉。用 dsu on tree 统计即可。

#include 

using namespace std;

const int N = 100005;

pair<int, int> result;
int n, current;
bool color[N];

struct tree_t {
  int son[N], size[N];
  vector<int> adj[N];

  void dfs(int x, int parent) {
    size[x] = 1;
    for (auto y : adj[x]) {
      if (y != parent) {
        dfs(y, x);
        size[x] += size[y];
        if (size[y] > size[son[x]]) {
          son[x] = y;
        }
      }
    }
  }
} a, b;

void flip(int x, tree_t &a) {
  for (auto y : a.adj[x]) {
    if (color[y] == color[x]) {
      ++current;
    } else {
      --current;
    }
  }
  color[x] = !color[x];
}

void modify(int x, int parent, tree_t &a, tree_t &b) {
  flip(x, b);
  for (auto y : a.adj[x]) {
    if (y != parent) {
      modify(y, x, a, b);
    }
  }
}

void dfs(int x, int parent, bool keep, tree_t &a, tree_t &b) {
  for (auto y : a.adj[x]) {
    if (y != parent && y != a.son[x]) {
      dfs(y, x, false, a, b);
    }
  }
  if (a.son[x]) {
    dfs(a.son[x], x, true, a, b);
  }
  for (auto y : a.adj[x]) {
    if (y != parent && y != a.son[x]) {
      modify(y, x, a, b);
    }
  }
  flip(x, b);
  if (parent) {
    if (current < result.first) {
      result = make_pair(current, 0);
    }
    if (current == result.first) {
      ++result.second;
    }
  }
  if (!keep) {
    modify(x, parent, a, b);
  }
}

pair<int, int> solve(tree_t &a, tree_t &b) {
  a.dfs(1, 0);
  result = make_pair(n, 0);
  dfs(1, 0, false, a, b);
  ++result.first;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d", &n);
  for (int i = 1; i < n; ++i) {
    int x, y;
    scanf("%d %d", &x, &y);
    a.adj[x].push_back(y);
    a.adj[y].push_back(x);
  }
  for (int i = 1; i < n; ++i) {
    int x, y;
    scanf("%d %d", &x, &y);
    b.adj[x].push_back(y);
    b.adj[y].push_back(x);
  }
  solve(a, b);
  if (result.first == 2) {
    printf("2 %d\n", result.second);
    return 0;
  }
  int answer = 0;
  if (result.first == 3) {
    answer += result.second;
  }
  solve(b, a);
  if (result.first == 3) {
    answer += result.second;
  }
  printf("3 %d\n", answer);
  return 0;
}

你可能感兴趣的:(CodeForces Gym 101669 简要题解)