1. 程式人生 > >AGC002 簡要題解

AGC002 簡要題解

傳遞 auto begin ati bit 檢查 code cin int

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;
}

AGC002 簡要題解