【CodeForces】CodeForces Round #402 (Div. 1) 题解

【比赛链接】

【题解链接】

**【A】**String Game

【思路要点】

  • 显然可以二分答案。
  • 然后判定 p 是否为删减后的 t 的子序列即可。
  • 时间复杂度 O ( N L o g N )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 200005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
int n, m, a[MAXN];
char s[MAXN], t[MAXN];
bool flg[MAXN];
bool check(int mid) {
  memset(flg, 0, sizeof(flg));
  for (int i = 1; i <= mid; i++)
      flg[a[i]] = true;
  int pos = 1;
  for (int i = 1; i <= m; i++) {
      while (pos <= n && !(!flg[pos] && s[pos] == t[i])) pos++;
      if (pos > n) return false; pos++;
  }
  return true;
}
int main() {
  scanf("\n%s\n%s", s + 1, t + 1);
  n = strlen(s + 1);
  m = strlen(t + 1);
  for (int i = 1; i <= n; i++)
      read(a[i]);
  int l = 0, r = n - m;
  while (l < r) {
      int mid = (l + r + 1) / 2;
      if (check(mid)) l = mid;
      else r = mid - 1;
  }
  writeln(l);
  return 0;
}

**【B】**Bitwise Formula

【思路要点】

  • 容易发现问题对于每一位是独立的。
  • 对于每一位,枚举其取值 ( 0 / 1 ) ,模拟一遍题目中的过程,取较最优者作为答案。
  • 时间复杂度 O ( N M )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 5005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
int n, m, now[MAXN];
int x[MAXN], y[MAXN];
string name, rubbish, Min, Max;
string opt[MAXN], val[MAXN];
map <string, int> num;
int main() {
  read(n), read(m);
  for (int i = 1; i <= n; i++) {
      cin >> name;
      num[name] = i;
      cin >> rubbish;
      cin >> val[i];
      if (val[i][0] == '0' || val[i][0] == '1') continue;
      else {
          x[i] = num[val[i]];
          cin >> opt[i];
          cin >> rubbish;
          y[i] = num[rubbish];
      }
  }
  for (int j = 0; j < m; j++) {
      int sumz = 0, sumo = 0;
      now[0] = 0;
      for (int i = 1; i <= n; i++) {
          if (opt[i] == "OR") now[i] = now[x[i]] | now[y[i]];
          else if (opt[i] == "AND") now[i] = now[x[i]] & now[y[i]];
          else if (opt[i] == "XOR") now[i] = now[x[i]] ^ now[y[i]];
          else now[i] = val[i][j] - '0';
          sumz += now[i];
      }
      now[0] = 1;
      for (int i = 1; i <= n; i++) {
          if (opt[i] == "OR") now[i] = now[x[i]] | now[y[i]];
          else if (opt[i] == "AND") now[i] = now[x[i]] & now[y[i]];
          else if (opt[i] == "XOR") now[i] = now[x[i]] ^ now[y[i]];
          else now[i] = val[i][j] - '0';
          sumo += now[i];
      }
      if (sumz <= sumo) Min += '0';
      else Min += '1';
      if (sumz >= sumo) Max += '0';
      else Max += '1';
  }
  cout << Min << endl;
  cout << Max << endl;
  return 0;
}

**【C】**Peterson Polyglot

【思路要点】

  • 我们本质上希望求出的是对于每一个点 i ,若将其所有儿子全部删除,将其孙子连接至其下并整理(合并相同的字符)后会消失的节点数 b e n i f i t i ,如果得到了 b e n i f i t i ,那么我们只需要找到 b e n i f i t i 的总和最大的一层节点,将它们的儿子删去即可。

  • 考虑一个直观的做法:

    对于每个点 i ,首先令 b e n i f i t i 为其儿子数。

    考虑整理字典树的过程,将其儿子中所有带有出边 c h ( c h = a , b , c , . . . , z ) 的点找到,令这样的点数为 c n t ,若 c n t 2 ,将 b e n i f i t i 增加 c n t 1 ,并递归整理这些点的 c h 出边指向的节点。

  • 考虑其复杂度,可以发现,该算法的整理部分涉及到的总点数为 O ( b e n i f i t i )

    并且在点 i 处被整理的点数不会超过以点 i 为根的子树大小减去点 i 最大的子树的大小。

    也就是说,一个点一旦在 b e n i f i t i 中被计算一次,计算它的子树大小就会至少翻倍。

    因此,一个点在 b e n i f i t i 中被计算的次数为 O ( L o g N ) 次,该算法的整理部分涉及到的总点数为 O ( b e n i f i t i ) = O ( N L o g N )

  • 时间复杂度 O ( 26 N L o g N )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 300005;
const int MAXC = 26;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
struct edge {int dest; char val; };
vector <edge> a[MAXN];
vector <int> p[MAXN];
int n, benifit[MAXN];
int child[MAXN][MAXC], depth[MAXN];
int calc(int depth) {
  if (p[depth].size() <= 1) return 0;
  int ans = 0;
  for (int j = 0; j < 26; j++) {
      p[depth + 1].clear();
      int cnt = 0;
      for (unsigned i = 0; i < p[depth].size(); i++)
          if (child[p[depth][i]][j]) {
              cnt++;
              p[depth + 1].push_back(child[p[depth][i]][j]);
          }
      ans += max(cnt - 1, 0) + calc(depth + 1);
  }
  return ans;
}
void dfs(int pos, int fa) {
  depth[pos] = depth[fa] + 1;
  for (unsigned i = 0; i < a[pos].size(); i++)
      if (a[pos][i].dest != fa) {
          child[pos][a[pos][i].val - 'a'] = a[pos][i].dest;
          dfs(a[pos][i].dest, pos);
      }
}
int main() {
  read(n);
  for (int i = 1; i <= n - 1; i++) {
      int x, y; char z;
      scanf("%d%d %c", &x, &y, &z);
      a[x].push_back((edge) {y, z});
      a[y].push_back((edge) {x, z});
  }
  dfs(1, 0);
  for (int i = 1; i <= n; i++) {
      p[0].clear();
      for (int j = 0; j < MAXC; j++)
          if (child[i][j]) {
              benifit[depth[i]]++;
              p[0].push_back(child[i][j]);
          }
      benifit[depth[i]] += calc(0);
  }
  int ans = 1;
  for (int i = 1; i <= n; i++)
      if (benifit[i] > benifit[ans]) ans = i;
  writeln(n - benifit[ans]);
  writeln(ans);
  return 0;
}

**【D】**Parquet Re-laying

【思路要点】

  • 我们发现难以轻易地构造出一组无解的情况,因此我们猜想所有合法的状态都是能够相互到达的。

  • 由于操作可逆,我们可以试图将给出的两个状态分别归一与一个统一的中间状态。

  • N M 必然至少有一个是偶数,不妨令 N 为偶数( M 为偶数的情况是对称的),我们希望将每个状态归一于所有地板竖直摆放的状态。

  • 考虑最左边的列,若所有地板已经竖直摆放,那么将这列删去(不予考虑)

    否则,最左边的列可能包含一些水平摆放的地板的左半段,并且由于 N 是偶数,这样的左半段的个数也为偶数,它们两两之间由竖直摆放的地板连接,相隔的距离也为偶数。

    若一对竖直对齐的水平摆放的地板中间(两列)仅有竖直摆放的地板,那么显然我们可以将这些地板通过操作变为水平摆放的,再将这一段内的所有地板通过操作变为竖直摆放的,这样就消除了一对竖直对齐的水平摆放的地板,使用的操作次数是 O ( N ) 的。

    可以证明若当前还存在水平摆放的地板,那么一定存在上述的一对竖直对齐的水平摆放的地板,使得它们中间仅有竖直摆放的地板。证明较为显然,因为一对竖直对齐的水平摆放的地板中间若存在水平摆放的地板,一定可以找到一对距离更近的竖直对齐的水平摆放的地板。

  • 那么算法就很明显了,暴力找到上述的一对竖直对齐的水平摆放的地板,花费 O ( N ) 的操作次数消除它们,重复执行直到不存在水平摆放的地板。

  • 执行次数至多 O ( N 2 ) ,单次花费时间至多 O ( N 3 )

  • 时间复杂度 O ( N 5 ) ,使用操作次数上限 O ( N 3 )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 55;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
int n, m;
char s[MAXN][MAXN];
vector <int> ansx[2], ansy[2];
void getplan(vector <int> &ansx, vector <int> &ansy) {
  if (n % 2 == 0) {
      int cnt = 0;
      for (int i = 1; i <= n; i++)
      for (int j = 1; j <= m; j++)
          cnt += s[i][j] == 'L';
      while (cnt != 0) {
          int px = 0, py = 0, qx = 0, qy = 0;
          for (int i = 1; i <= n; i++)
          for (int j = 1; j <= m; j++)
              if (s[i][j] == 'L') {
                  bool flg = false;
                  for (int k = i + 1; k <= n; k++)
                      if (s[k][j] == 'L') {
                          flg = true;
                          qx = k, qy = j;
                          break;
                      } else if (s[k][j] == 'R' || s[k][j + 1] == 'L') break;
                  if (flg) px = i, py = j;
              }
          if (px == 0) {
              printf("Error!\n");
              exit(0);
          }
          for (int i = px + 1; i <= qx - 1; i += 2) {
              ansx.push_back(i);
              ansy.push_back(py);
          }
          for (int i = px; i <= qx; i += 2) {
              ansx.push_back(i);
              ansy.push_back(py);
              s[i][py] = s[i][py + 1] = 'U';
              s[i + 1][py] = s[i + 1][py + 1] = 'D';
          }
          cnt -= 2;
      }
  } else {
      int cnt = 0;
      for (int i = 1; i <= n; i++)
      for (int j = 1; j <= m; j++)
          cnt += s[i][j] == 'U';
      while (cnt != 0) {
          int px = 0, py = 0, qx = 0, qy = 0;
          for (int i = 1; i <= n; i++)
          for (int j = 1; j <= m; j++)
              if (s[i][j] == 'U') {
                  bool flg = false;
                  for (int k = j + 1; k <= m; k++)
                      if (s[i][k] == 'U') {
                          flg = true;
                          qx = i, qy = k;
                          break;
                      } else if (s[i][k] == 'D' || s[i + 1][k] == 'U') break;
                  if (flg) px = i, py = j;
              }
          if (px == 0) {
              printf("Error!\n");
              exit(0);
          }
          for (int i = py + 1; i <= qy - 1; i += 2) {
              ansx.push_back(px);
              ansy.push_back(i);
          }
          for (int i = py; i <= qy; i += 2) {
              ansx.push_back(px);
              ansy.push_back(i);
              s[px][i] = s[px + 1][i] = 'L';
              s[px][i + 1] = s[px + 1][i + 1] = 'R';
          }
          cnt -= 2;
      }
  }
}
int main() {
  read(n), read(m);
  for (int i = 1; i <= n; i++)
      scanf("\n%s", s[i] + 1);
  getplan(ansx[0], ansy[0]);
  for (int i = 1; i <= n; i++)
      scanf("\n%s", s[i] + 1);
  getplan(ansx[1], ansy[1]);
  writeln(ansx[0].size() + ansx[1].size());
  for (unsigned i = 0; i < ansx[0].size(); i++)
      printf("%d %d\n", ansx[0][i], ansy[0][i]);
  reverse(ansx[1].begin(), ansx[1].end());
  reverse(ansy[1].begin(), ansy[1].end());
  for (unsigned i = 0; i < ansx[1].size(); i++)
      printf("%d %d\n", ansx[1][i], ansy[1][i]);
  return 0;
}

**【E】**Selling Numbers

【思路要点】

  • 由于要考虑进位的问题,我们考虑从低位向高位DP。
  • 我们发现无论如何,我们加上的数是对于每个 B i 相同的,因此 B i 相对的大小关系不会产生变化。
  • 那么,无论我们加上什么数,从第 i 位向第 i + 1 位有进位的数一定是第 i 位到第一位组成的数较大的一些数,因此,对于每一位,可能的产生进位的集合只有 O ( N ) 个。
  • d p i , j 表示已经考虑了所有数较低的 i 位,其中有 j 个数产生了进位时,能够获得的最大价值。
  • d p i , 为一层,从 d p i , d p i + 1 , 之间的转移我们可以较为简单地做到 O ( 10 N ) 的时间复杂度,完成一层的转移后,对所有数第 i + 1 位到第一位组成的数重新排序即可。
  • 时间复杂度 O ( N M a x { | A | , | B i | } ( L o g N + 10 ) )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 1005;
const int INF = 1e9;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
int n, m, Max, type, val[MAXN], len[MAXN], home[MAXN];
int a[MAXN][MAXN], b[MAXN], dp[MAXN][MAXN];
char s[MAXN];
bool cmp(int x, int y) {
  if (a[x][type] == a[y][type]) return home[x] < home[y];
  else return a[x][type] > a[y][type];
}
int main() {
  scanf("%s", s + 1), read(n);
  m = strlen(s + 1);
  reverse(s + 1, s + m + 1);
  for (int i = 1; i <= m; i++)
      if (s[i] == '?') b[i] = -1;
      else b[i] = s[i] - '0';
  int Max = m;
  for (int i = 1; i <= n; i++) {
      scanf("\n%s", s + 1);
      len[i] = strlen(s + 1);
      reverse(s + 1, s + len[i] + 1);
      for (int j = 1; j <= len[i]; j++)
          a[i][j] = s[j] - '0';
      chkmax(len[i], m);
      chkmax(Max, len[i]);
  }
  Max++;
  for (int i = 0; i <= 9; i++)
      read(val[i]);
  for (int i = 0; i <= Max; i++)
  for (int j = 0; j <= n; j++)
      dp[i][j] = -INF;
  dp[0][0] = 0;
  static int now[MAXN];
  for (int i = 1; i <= n; i++)
      now[i] = i;
  for (int i = 1; i <= Max; i++) {
      int from, to;
      if (b[i] == -1) {
          if (i == m) from = 1, to = 9;
          else from = 0, to = 9;
      } else from = to = b[i];
      for (int ch = from; ch <= to; ch++) {
          int sum = 0, cnt = 0;
          for (int j = 1; j <= n; j++)
              if (i <= len[j]) {
                  sum += val[(a[j][i] + ch) % 10];
                  if (a[j][i] + ch >= 10) cnt++;
              }
          chkmax(dp[i][cnt], dp[i - 1][0] + sum);
          for (int j = 1; j <= n; j++) {
              if (i <= len[now[j]]) {
                  sum -= val[(a[now[j]][i] + ch) % 10];
                  if (a[now[j]][i] + ch >= 10) cnt--;
              }
              sum += val[(a[now[j]][i] + ch + 1) % 10];
              if (a[now[j]][i] + ch + 1 >= 10) cnt++;
              chkmax(dp[i][cnt], dp[i - 1][j] + sum);
          }
      }
      for (int j = 1; j <= n; j++)
          home[now[j]] = j;
      type = i;
      sort(now + 1, now + n + 1, cmp);
  }
  writeln(dp[Max][0]);
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/81414669