AGC002 简要题解

A

分情况讨论一下。。。

#include <bits/stdc++.h>

using namespace std;

int main() {
  int a, b;
  scanf("%d %d", &a, &b);
  if (a <= 0 && b >= 0) {
    puts("Zero");
  } else if (b < 0) {
    puts(((b - a + 1) & 1) ? "Negative" : "Positive");
  } else {
    puts("Positive");
  }
  return 0;
}

B

转移球就把是否有红球的标记在盒子间传递一下,注意转移后盒子为空的情况。

#include <bits/stdc++.h>

using namespace std;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int n, m;
  cin >> n >> m;
  vector<int> sz(n, 1);
  vector<uint8_t> c(n);
  c[0] = 1;
  for (int i = 0; i < m; i++) {
    int x, y;
    cin >> x >> y;
    x--; y--;
    c[y] |= c[x];
    sz[x]--;
    sz[y]++;
    if (sz[x] == 0) {
      c[x] = 0;
    }
  }
  int ans = 0;
  for (int i = 0; i < n; i++) {
    ans += c[i];
  }
  cout << ans << endl;
  return 0;
}

C

错误的贪心:为了使剩下的最大,每次都删掉两边最小的那个。

反例:40 50 1 60,若删掉两个,删去 60 + 1 显然比删去 40 + 50 更优。。。

正确的贪心:注意到删到最后一步时还剩下两根绳子,若有解的话两根绳子的长度和一定不小于 \(L\),于是可以找这样的相邻两根绳子,剩下的从外到内删就好了。

#include <bits/stdc++.h>

using namespace std;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int n, len;
  cin >> n >> len;
  vector<int> a(n);
  for (int i = 0; i < n; i++) {
    cin >> a[i];
  }
  int ii = 0, mx = 0;
  for (int i = 0; i < n - 1; i++) {
    if (a[i] + a[i + 1] > mx) {
      mx = a[i] + a[i + 1];
      ii = i;
    }
  }
  if (mx < len) {
    cout << "Impossible\n";
    return 0;
  }
  cout << "Possible\n";
  for (int i = 0; i < ii; i++) {
    cout << i + 1 << '\n';
  }
  for (int i = n - 2; i > ii; i--) {
    cout << i + 1 << '\n';
  }
  cout << ii + 1 << '\n';
  return 0;
}

D

考虑一个 naive 的询问过程:把边按照边权从小到大的顺序插入并查集,每插一条就检查一下两个询问点 \(x, y\) 能够到达的点个数是否大于等于 \(z\)

这个过程可以对多个询问产生贡献,可以反复这样做的同时对所有询问进行二分。

#include <bits/stdc++.h>

using namespace std;

int main() {
  int n, m;
  scanf("%d %d", &n, &m);
  vector<int> a(m), b(m);
  for (int i = 0; i < m; i++) {
    scanf("%d %d", &a[i], &b[i]);
    a[i]--; b[i]--;
  }
  int tt;
  scanf("%d", &tt);
  vector<int> qa(tt), qb(tt), qc(tt);
  for (int i = 0; i < tt; i++) {
    scanf("%d %d %d", &qa[i], &qb[i], &qc[i]);
    qa[i]--; qb[i]--;
  }
  vector<int> ql(tt), qr(tt);
  for (int i = 0; i < tt; i++) {
    ql[i] = -1;
    qr[i] = m - 1;
  }
  vector<int> p(n), sz(n);
  vector< vector<int> > id(m);
  function<int(int)> root = [&](int x) {
    return x == p[x] ? x : (p[x] = root(p[x]));
  };
  auto unite = [&](int x, int y) {
    x = root(x);
    y = root(y);
    if (x == y) {
      return;
    }
    p[x] = y;
    sz[y] += sz[x];
    assert(sz[y] <= n);
  };
  auto real_size = [&](int x, int y) {
    x = root(x);
    y = root(y);
    if (x != y) {
      return sz[x] + sz[y];
    }
    return sz[x];
  };
  for (int it = 0; it < 20; it++) {
    for (int i = 0; i < m; i++) {
      id[i].clear();
    }
    for (int i = 0; i < n; i++) {
      p[i] = i;
      sz[i] = 1;
    }
    for (int i = 0; i < tt; i++) {
      if (qr[i] - ql[i] > 1) {
        int mi = (ql[i] + qr[i]) >> 1;
        id[mi].push_back(i);
      }
    }
    for (int i = 0; i < m; i++) {
      unite(a[i], b[i]);
      for (int& qi : id[i]) {
        if (real_size(qa[qi], qb[qi]) >= qc[qi]) {
          qr[qi] = i;
        } else {
          ql[qi] = i;
        }
      }
    }
  }
  for (int i = 0; i < tt; i++) {
    printf("%d\n", qr[i] + 1);
  }
  return 0;
}

E

做法太神了。。。但是没图不是很好理解,还是见题解吧。。。

http://agc002.contest.atcoder.jp/data/agc/002/editorial.pdf

抽象成这样一个图注意到斜对角的胜负态都一样,于是可以在图上一直往右上角走到死,然后看下剩下的两个方向能不能走到必胜态。。。

#include <bits/stdc++.h>

using namespace std;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int n;
  cin >> n;
  vector<int> a(n);
  for (int i = 0; i < n; i++) {
    cin >> a[i];
    a[i]--;
  }
  sort(a.begin(), a.end(), greater<int>());
  int i = 0, j = 0;
  while (i < n - 1 && a[i + 1] > j) {
    i++;
    j++;
  }
  int x = a[i] - j;
  int y = 0;
  while (i < n - 1 && j == a[i + 1]) {
    i++;
    y++;
  }
  if (x % 2 == 1 || y % 2 == 1) {
    cout << "First\n";
  } else {
    cout << "Second\n";
  }
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/arg-53/p/9164915.html
今日推荐