AtCoder Beginner Contest 258 题解

只有 ABCDEG 的题解。

A

模拟。

代码
void mian() {
  int x; scanf("%d", &x);
  if (x < 60) printf("21:%02d\n", x);
  else printf("22:%02d\n", x - 60);
}

B

模拟。

不要读错题!!不能拐弯!!

代码
const int N = 10;
const int dx[] = {-1, -1, -1, 0, 0, 1, 1, 1};
const int dy[] = {1, 0, -1, 1, -1, 1, 0, -1};

int n, a[N + 10][N + 10];
ll ans;

void mian() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
      scanf("%1d", &a[i][j]);
  for (int k = 0; k < 8; k++)
    for (int i = 1; i <= n; i++)
      for (int j = 1; j <= n; j++) {
        int x = i, y = j;
        ll now = 0;
        for (int step = 1; step <= n; step++) {
          now = now * 10 + a[x][y];
          int nx = x + dx[k], ny = y + dy[k];
          if (nx == 0) nx = n;
          if (ny == 0) ny = n;
          if (nx == n + 1) nx = 1;
          if (ny == n + 1) ny = 1;
          x = nx, y = ny;
        }
        ans = max(ans, now);
    }
  printf("%lld\n", ans);
}

C

维护一下每次操作 \(1\) 后字符串的开始位置在原字符串的位置即可。

代码
const int N = 5e5;

int n, q;
char s[N + 10];

void mian() {
  scanf("%d%d%s", &n, &q, s);
  int p = 0;
  while (q--) {
    int opt, x; scanf("%d%d", &opt, &x);
    if (opt == 1) p = (p - x) % n;
    else printf("%c\n", s[((p + x - 1) % n + n) % n]);
  }
}

D

首先枚举要玩的前缀 \(1\sim i\)。然后考虑怎么分配这 \(x\) 次。

贪心:\(b\) 最小的要被分到的最多,可以用微调法证明。

代码
const int N = 2e5;

int n, a[N + 10], b[N + 10], m;
ll sum[N + 10];
int mn[N + 10];

void mian() {
  scanf("%d%d", &n, &m);
  mn[0] = 0x3f3f3f3f;
  for (int i = 1; i <= n; i++) {
    scanf("%d%d", a + i, b + i);
    sum[i] = sum[i - 1] + a[i] + b[i];
    mn[i] = min(mn[i - 1], b[i]);
  }
  ll ans = 0x3f3f3f3f3f3f3f3f;
  for (int i = 1; i <= n; i++)
    ans = min(ans, sum[i - 1] + a[i] + 1LL * mn[i] * (m - i + 1));
  printf("%lld\n", ans);
}

E

预处理以每个 \(1\le i\le n\) 作为“起点”时,对应的“终点”是什么。然后倍增。

注意这个题最大的一个坑是“终点”和“起点”间的距离可以是 \(10^9\),所以不能简简单单的只处理前缀和!!

代码
const int N = 2e5, K = 40;

int n, q, x, a[N + 10], res[N + 10], to[K + 5][N + 10];
ll sum[N * 2 + 10], sum2[N + 10];

ll getsum(int l, int r) {
  l--, r--;
  int l1 = l / n, l2 = l % n;
  int r1 = r / n, r2 = r % n;
  if (l1 == r1) return sum[r2 + 1] - sum[l2];
  else return sum2[l2 + 1] + sum[r2 + 1] + (r1 - l1 - 1) * sum[n];
}

void count(int p) {
  int l = p, r = p + 1000000000, _l = l;
  while (l < r) {
    int mid = (l + r) >> 1;
    if (getsum(_l, mid) >= 1LL * x) r = mid;
    else l = mid + 1;
  }
  res[p] = l - p + 1;
  to[0][p] = l + 1;
  while (to[0][p] > n) to[0][p] -= n;
}

void mian() {
  scanf("%d%d%d", &n, &q, &x);
  for (int i = 1; i <= n; i++)
    scanf("%d", a + i), sum[i] = sum[i - 1] + a[i];
  for (int i = n; i >= 1; i--)
    sum2[i] = sum2[i + 1] + a[i];
  for (int i = 1; i <= n; i++)
    count(i);
  for (int j = 1; j <= K; j++)
    for (int i = 1; i <= n; i++)
      to[j][i] = to[j - 1][to[j - 1][i]];
  while (q--) {
    ll k; scanf("%lld", &k);
    k--;
    int now = 1;
    for (int i = K; ~i; i--)
      if (k - (1LL << i) >= 0)
        now = to[i][now], k -= (1LL << i);
    printf("%d\n", res[now]);
  }
}

G

枚举 \(1\le i\lt j\le n\)\(a_{i,j}=1\),考虑有多少个 \(k\) 满足 \(a_{i,k}=a_{j,k}=1\) 都有边。

发现这个东西就是 \(|S_i\cap S_j|\),其中 \(S_i\) 表示满足 \(j\gt i,a_{i,j}=1\)\(j\) 的集合,于是可以用 std::bitset 处理。

代码
const int N = 3000;

int n;
int a[N + 10][N + 10];
bitset<N + 10> S[N + 10];

void mian() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
      int x; scanf("%1d", &x);
      a[i][j] = x;
      if (j > i) S[i].set(j, x);
    }
  }
  ll ans = 0;
  for (int i = 1; i <= n; i++)
    for (int j = i + 1; j <= n; j++)
      if (a[i][j]) ans += (S[i] & S[j]).count();
  printf("%lld\n", ans);
}
posted @ 2022-07-02 22:31  registerGen  阅读(306)  评论(0)    收藏  举报