2025-9-29 总结(ZJCPC 2017 重现赛)

A - Cooking Competition

情况

  • 时间:\(10min\)
  • 预期:\(\text{AC}\)
  • 实际:\(\text{AC}\)

知识点

  • 贪心

思路

我们可以发现 \(3\)\(4\) 的操作对于两人分数的差没有任何影响,所以我们只需要统计操作 \(1\)\(2\) 的数量即可,如果 \(1\)\(2\) 数量多则输出 Kobayashi\(1\) 的数量比 \(2\) 少则输出 Tohru,相等则输出 Draw

代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

int t, n, cnt1, cnt2;

signed main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  for (cin >> t; t--; cnt1 = cnt2 = 0) {
    cin >> n;
    for (int i = 1, x; i <= n; i++) {
      cin >> x, cnt1 += x == 1, cnt2 += x == 2;
    }
    cout << (cnt1 < cnt2 ? "Tohru\n" : (cnt1 > cnt2 ? "Kobayashi\n" : "Draw\n"));
  }
  return 0;
}

B - Problem Preparation

情况

  • 时间:\(10min\)
  • 预期:\(\text{AC}\)
  • 实际:\(\text{AC}\)

知识点

  • 贪心

思路

我们需要判断排序过的数组 \(a\) 以下条件是否全部满足,如果都成立则输出 Yes 否则输出 No

  1. \(10\le n\le13\)
  2. 第一个数大于等于 \(1\)
  3. 第二个数小于等于 \(1\)
  4. \(a_i-a_{i-1}\le 2(2\le i\le n-1)\)

代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int kMaxN = 110;

int a[kMaxN], t, n;

signed main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  for (cin >> t; t--;) {
    cin >> n;
    for (int i = 1; i <= n; i++) {
      cin >> a[i];
    }
    sort(a + 1, a + 1 + n);
    if (a[1] < 1 || a[2] > 1 || n < 10 || n > 13) {
      cout << "No\n";
    } else {
      int flag = 1;
      for (int i = 2; i < n; i++) {
        flag &= a[i] - a[i - 1] <= 2;
      }
      cout << (flag ? "Yes\n" : "No\n");
    }
  }
  return 0;
}

C - What Kind of Friends Are You?

情况

  • 时间:\(30min\)
  • 预期:\(\text{AC}\)
  • 实际:\(\text{AC}\)

知识点

  • 状压

思路

由于 \(q\) 十分小,所以我们考虑对于每一个名称,如果第 \(j\) 个字符串,在问题 \(i(0\le i<q)\) 中出现过,那么给 \(j\) 加上 \(2^i\),所以最后查询就只需要 \(O(n)\) 了。

代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int kMaxN = 210;

int t, n, m, x;
string s[kMaxN], c;
map<string, int> mp;
map<int, vector<string> > mp2;

signed main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  for (cin >> t; t--; mp.clear(), mp2.clear()) {
    cin >> n >> m >> x;
    for (int i = 1; i <= x; i++) {
      cin >> s[i];
    }
    for (int i = 0, k; i < m; i++) {
      for (cin >> k; k--;) {
        cin >> c, mp[c] += 1ll << i;
      }
    }
    for (int i = 1; i <= x; i++) {
      mp2[mp[s[i]]].push_back(s[i]);
    }
    for (int i = 1; i <= n; i++) {
      int sum = 0;
      for (int j = 0, k; j < m; j++) {
        cin >> k, sum += k * (1ll << j);
      }
      if (!mp2[sum].size() || mp2[sum].size() > 1) {
        cout << "Let's go to the library!!\n";
      } else {
        cout << mp2[sum][0] << '\n';
      }
    }
  }
  return 0;
}

D - Let's Chat

情况

  • 时间:\(20min\)
  • 预期:\(\text{AC}\)
  • 实际:\(\text{AC}\)

知识点

  • 暴力

思路

我们可以枚举 \(x\)\(y\) 中的两段,看他们的交集,我们可以证明,不可能存在四个段 \(a,b,c,d(a,b\in x,c,d\in y,a\neq b,c\neq d)\),使得他们的交集的交集大于等于 \(1\),所以我们统计出每一个交集(假设长度为 \(len\)),直接把答案加上 \(\max(0,len-m+1)\) 即可。

代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int kMaxN = 210;

int t, n, m, x, y, ans;
pair<int, int> p[2][kMaxN];

signed main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  for (cin >> t; t--; ans = 0) {
    cin >> n >> m >> x >> y;
    for (int i = 1; i <= x; i++) {
      cin >> p[0][i].first >> p[0][i].second;
    }
    for (int i = 1; i <= y; i++) {
      cin >> p[1][i].first >> p[1][i].second;
    }
    for (int i = 1; i <= x; i++) {
      for (int j = 1; j <= y; j++) {
        int l = max(p[0][i].first, p[1][j].first), r = min(p[0][i].second, p[1][j].second);
        ans += max(0ll, r - l - m + 2);
      }
    }
    cout << ans << '\n';
  }
  return 0;
}

E - Seven Segment Display

情况

  • 时间:\(20min\)
  • 预期:\(\text{AC}\)
  • 实际:\(\text{AC}\)

知识点

  • \(\text{dp}\)

思路

我们发现其实答案只有两种情况 \(n+s\ge16^8\) 要拆成两个部分,\(n+s<16^8\) 直接计算一个部分的答案即可,我们假设 \(S(x)\) 函数是用来计算 \(0~x\) 的和的,所以答案有两种:

  1. 答案为 \(S(FFFFFFFF)-S(s-1)+S(n+s-16^8)\)
  2. 答案为 \(S(n+s)-S(s-1)\)

所以我们现在思考如何计算 \(S\),但只要有脑袋的人肯定会想到数位 \(\text{dp}\),然后就解决了。

代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int kMaxN = 110, fx[] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6, 6, 5, 4, 5, 5, 4};

int b[kMaxN], dp[kMaxN][kMaxN], t, n, m, fac;
string s;

int D(int x, int sum, int flag) {
  if (x < 0) {
    return sum;
  }
  if (!flag && dp[x][sum] != -1) {
    return dp[x][sum];
  }
  int cnt = 0;
  for (int i = 0; i <= (flag ? b[x] : 15); i++) {
    cnt += D(x - 1, sum + fx[i], flag && i == (flag ? b[x] : 15));
  }
  return flag ? cnt : dp[x][sum] = cnt;
}

int S(int x) {
  if (x < 0) {
    return 0;
  }
  int pos = 0;
  for (memset(b, 0, sizeof b); x > 0; b[pos++] = x % 16, x /= 16);
  return D(7, 0, 1);
}

signed main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  for (int i = 0; i < 8; i++) {
    fac = fac * 16 + 15;
  }
  for (cin >> t, memset(dp, -1, sizeof dp); t--; m = 0) {
    cin >> n >> s;
    for (int i = 0; i < 8; i++) {
      m = m * 16 + ('0' <= s[i] && s[i] <= '9' ? s[i] - '0' : s[i] - 'A' + 10);
    }
    cout << (m + n >= fac ? S(fac) - S(m - 1) + S(m + n - fac - 2) : S(m + n - 1) - S(m - 1)) << '\n';
  }
  return 0;
}

F - Heap Partition

情况

  • 时间:\(30min\)
  • 预期:\(\text{AC}\)
  • 实际:\(\text{AC}\)

知识点

  • 贪心,\(\text{set}\)

思路

从前往后遍历序列,对于当前元素,尝试将其插入到已有的堆中。如果当前元素无法插入任何已有堆,则创建一个新堆。使用 \(\text{set}\) 维护堆的节点序列,利用 upper_bound 快速找到插入位置。如果一个节点已经有两个子节点,则从 \(\text{set}\) 中移除该节点即可,这样就可以在 \(O(n\log n)\) 的时间里解决了。

代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int kMaxN = 1e5 + 10;

int a[kMaxN], deg[kMaxN], t, n, cnt;
vector<int> v[kMaxN];

struct node {
  int sum, pos;

  friend bool operator<(node x, node y) {
    return a[x.pos] == a[y.pos] ? x.pos < y.pos : a[x.pos] < a[y.pos];
  }
};


set<node> s;

signed main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  for (cin >> t; t--; s.clear(), cnt = 0) {
    cin >> n, fill(deg, deg + 1 + n, 0);
    node tmp;
    for (int i = 1; i <= n; i++) {
      cin >> a[i], tmp.pos = i;
      auto w = s.upper_bound(tmp);
      if (w == s.begin()) {
        v[cnt].push_back(tmp.pos), tmp.sum = cnt, s.insert(tmp), cnt++;
      } else {
        w--, tmp = *w, deg[tmp.pos]++, (deg[tmp.pos] == 2) && (s.erase(w), 0), tmp.pos = i, s.insert(tmp), v[tmp.sum].push_back(i);
      }
    }
    cout << cnt << '\n';
    for (int i = 0; i < cnt; i++) {
      cout << v[i].size();
      for (int j = 0; j < v[i].size(); j++) {
        cout << ' ' << v[i][j];
      }
      cout << '\n', v[i].clear();
    }
  }
  return 0;
}

G - Yet Another Game of Stones

情况

  • 时间:\(40min\)
  • 预期:\(\text{AC}\)
  • 实际:\(\text{AC}\)

知识点

  • 博弈论

思路

这道题我们需要分类讨论:

  1. \(a_i\) 是奇数,\(b_i=2\),无论如何A都输;
  2. \(a_i\) 是偶数,\(b_i=2\),A 一定要在第一次就全取完这一堆,否则 B 可以把这一堆变成情况 \(1\)
  3. \(a_i\) 是奇数且大于一个,\(b_i=1\),A 如果不一次取完,B 可以取得剩下 \(2\) 个,使得 A 输;
  4. \(a_i\) 是偶数,\(b_i=1\)。A 如果不取得剩下 \(1\) 个, B 可以取得剩下 \(2\) 个,使得 A 输;
  5. \(b_i=0\),两人可以取任意数量,所以题目变成了普通的 \(\text{nim}\) 博弈。

所以这道题就做完了。

代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int kMaxN = 1e5 + 10;

int a[kMaxN], b[kMaxN], t, n, sum, cnt1, cnt2;

signed main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  for (cin >> t; t--; sum = cnt1 = cnt2 = 0) {
    cin >> n;
    for (int i = 1; i <= n; i++) {
      cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
      cin >> b[i];
    }
    for (int i = 1; i <= n; i++) {
      cnt2 += a[i] & 1 && b[i] == 2;
      if ((a[i] > 1 && b[i] == 1) || (!(a[i] & 1) && b[i] == 2)) {
        cnt1++, a[i] = !(a[i] & 1) && b[i] == 1;
      }
    }
    if (cnt2 || cnt1 >= 2) {
      cout << "Bob\n";
    } else {
      for (int i = 1; i <= n; i++) {
        sum = sum ^ a[i];
      }
      cout << ((!cnt1 && sum) || (cnt1 && !sum) ? "Alice\n" : "Bob\n");
    }
  }
  return 0;
}

H - Binary Tree Restoring

情况

  • 时间:\(1.5h\)
  • 预期:\(\text{AC}\)
  • 实际:\(\text{AC}\)

知识点

  • \(\text{dfs}\)

思路

我们可以直接对 \(\text{dfs}\) 结果序列进行模拟,可以类似的参考树的前、中、后序互相转换的递归思路,所以只可能有两种情况:

  1. 当前节点在两个串中后面的节点假如不同则能确认两个子树;
  2. 如果相同则把下个点作当前点的一个儿子,如果子树中还有未连根的点则接到当前点下。

所以用深搜的方式不断分割左子树和右子树即可。

代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int kMaxN = 1e6 + 10;

int a[kMaxN], b[kMaxN], f1[kMaxN], f2[kMaxN], ans[kMaxN], t, n, cnt;

void S(int pos, int l, int r, int fa) {
  if (l <= r) {
    if (a[pos] == b[l]) {
      ans[a[pos]] = fa, cnt++, S(pos + 1, l + 1, r, a[pos]);
      if (cnt - pos <= r - l) {
        ans[a[cnt]] = fa, cnt++, S(cnt, l + cnt - pos, r, a[cnt - 1]);
      }
    } else {
      ans[a[pos]] = fa, cnt++, S(pos + 1, f2[a[pos]] + 1, f2[a[pos]] + f1[b[l]] - pos - 1, a[pos]);
      ans[b[l]] = fa, cnt++, S(f1[b[l]] + 1, l + 1, f2[a[pos]] - 1, b[l]);
    }
  }
}

signed main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  for (cin >> t; t--; cout << '\n') {
    cin >> n, cnt = 1;
    for (int i = 1; i <= n; i++) {
      cin >> a[i], f1[a[i]] = i;
    }
    for (int i = 1; i <= n; i++) {
      cin >> b[i], f2[b[i]] = i;
    }
    S(1, 1, n, 0);
    for (int i = 1; i <= n; i++) {
      cout << ans[i] << ' ';
    }
  }
  return 0;
}

K - Final Defense Line

情况

  • 时间:\(1h\)
  • 预期:\(\text{AC}\)
  • 实际:\(\text{AC}\)

知识点

  • 数学

思路

我们假设答案为 \(x,y,r\),所以根据题目的描述发现这道题就是要解一个方程:

\[\begin{equation}\left\{ \begin{array}{lr} (x-x_0)^2+(y-y_0)^2=(r-r_0)^2\\ (x-x_1)^2+(y-y_1)^2=(r-r_1)^2\\ (x-x_2)^2+(y-y_2)^2=(r-r_2)^2\\ \end{array} \right.\end{equation} \]

所以解方程就行了。

代码

#include <bits/stdc++.h>
#define int long long
#define double long double

using namespace std;

const double kEps = 1e-9;

int x[3], y[3], r[3], T;

int C(double a) { return a < -kEps ? -1 : (a > kEps ? 1 : 0); }

void S(double A, double B, double &a, double &b) {
  double d = sqrt(A * A + B * B), nxt_a = A / d * a + B / d * b, nxt_b = A / d * b - B / d * a;
  a = nxt_a, b = nxt_b;
}

signed main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  for (cin >> T; T--;) {
    for (int i = 0; i < 3; i++) {
      cin >> x[i] >> y[i] >> r[i];
    }
    double x1 = x[1] - x[0], y1 = y[1] - y[0], x2 = x[2] - x[0], y2 = y[2] - y[0];
    S(x1, y1, x2, y2), S(x1, y1, x1, y1);
    if (!C(y2)) {
      double a = 2 * (r[1] - r[0]) / x1 - 2 * (r[2] - r[0]) / x2, b = x1 + (r[0] * r[0] - r[1] * r[1]) / x1 - x2 - (r[0] * r[0] - r[2] * r[2]) / x2;
      if (!C(a)) {
        cout << (C(b) ? "0\n" : "-1\n");
      } else {
        double ans = -b / a;
        if (C(ans - max({0ll, r[0], r[1], r[2]})) < 0) {
          cout << "0\n";
        } else {
          double num = 2 * (r[1] - r[0]) * ans + x1 * x1 - r[1] * r[1] + r[0] * r[0];
          num /= 2 * x1;
          if (C((ans - r[0]) * (ans - r[0]) - num * num) < 0) {
            cout << "0\n";
          } else if (C((ans - r[0]) * (ans - r[0]) - num * num)) {
            cout << "2 " << fixed << setprecision(12) << ans << '\n';
          } else {
            cout << "1 " << fixed << setprecision(12) << ans << '\n';
          }
        }
      }
    } else {
      double a1 = -2 * x1, c1 = 2 * (r[1] - r[0]), d1 = x1 * x1 - r[1] * r[1] + r[0] * r[0], a2 = -2 * x2, b2 = -2 * y2, c2 = 2 * (r[2] - r[0]), d2 = x2 * x2 + y2 * y2 - r[2] * r[2] + r[0] * r[0];
      double X1 = -d1 / a1, Y1 = -c1 / a1, X2 = (a2 * d1 - a1 * d2) / (a1 * b2), Y2 = (a2 * c1 - a1 * c2) / (a1 * b2);
      double a = Y1 * Y1 + Y2 * Y2 - 1, b = (X1 * Y1 + X2 * Y2 + r[0]) * 2, c = X1 * X1 + X2 * X2 - r[0] * r[0];
      if (!C(a) && !C(b) && !C(c)) {
        cout << "-1\n";
      } else if (!C(a)) {
        if (!C(b)) {
          cout << "0\n";
        } else {
          double ans = -c / b;
          if (C(ans - max({0ll, r[0], r[1], r[2]})) < 0) {
            cout << "0\n";
          } else {
            cout << "1 " << fixed << setprecision(12) << ans << '\n';
          }
        }
      } else {
        double d = b * b - 4 * a * c;
        if (C(d) < 0) {
          cout << "0\n";
        } else if (!C(d)) {
          double ans = -b / (2 * a);
          if (C(ans - max({0ll, r[0], r[1], r[2]})) < 0) {
            cout << "0\n";
          } else {
            cout << "1 " << fixed << setprecision(12) << ans << '\n';
          }
        } else {
          double ans1 = (-b - sqrt(d)) / (2 * a), ans2 = (-b + sqrt(d)) / (2 * a);
          (ans1 > ans2) && (swap(ans1, ans2), 0), (!C(ans1 - ans2)) && (ans1 = -1);
          if (C(ans2 - max({0ll, r[0], r[1], r[2]})) < 0) {
            cout << "0\n";
          } else if (C(ans1 - max({0ll, r[0], r[1], r[2]})) < 0) {
            cout << "1 " << fixed << setprecision(12) << ans2 << '\n';
          } else {
            cout << "2 " << fixed << setprecision(12) << ans1 << '\n';
          }
        }
      }
    }
  }
  return 0;
}
posted @ 2025-09-29 16:02  小熊涛涛  阅读(73)  评论(0)    收藏  举报