20251229 - 月度检测总结

20251229 - 月度检测总结

考试情况

Rank : 13

Score : 4

Penalty : 414

题目 是否AC 罚时 补题情况
A Y 0 NULL
B Y 0 NULL
C N NULL Y
D N NULL Y
E N NULL Y
F Y -3 NULL
G N NULL N
H Y -3 NULL
I N NULL N

A - Cut the Array

考试时发现样例不对,调了半天,但是发现之前的代码是对的!浪费了 10 min。

思路:暴力枚举 \(l\)\(r\),再随便统计结果就好了。

void solve() {
  n = read();
  for (int i = 1; i <= n; i++) {
    s[i] = 0;
    a[i] = read();
    s[i] = s[i - 1] + a[i];
  }
  for (int i = 1; i <= n; i++) {
    for (int j = i + 1; j < n; j++) {
      int s1 = s[i] % 3;
      int s2 = (s[j] - s[i]) % 3;
      int s3 = (s[n] - s[j]) % 3;
      s1 %= 3;
      s2 %= 3;
      s3 %= 3;
      // printf("%d %d %d\n", s1, s2, s3);
      if ((s1 != s2 && s2 != s3 && s1 != s3) || (s1 == s2 && s2 == s3)) {
        printf("%d %d\n", i, j);
        return;
      }
    }
  }
  // puts("???");
  puts("0 0");
}

B - Maximum Cost Permutation

老师说要特判,但为什么我没有特判呢?

思路:想让排序的区间尽可能小,我们可以把较大的数字填到较小的空位里,这样就使得结果最大,最后的结果用两个指针扫一下即可。

set <int> st;
void solve() {
  st.clear();
  int n = read();
  for (int i = 1; i <= n; i++) {
    st.insert(-i);
    a[i] = read();
  }
  for (int i = 1; i <= n; i++) {
    if (a[i] != 0) {
      st.erase(-a[i]);
    }
  }
  for (int i = 1; i <= n; i++) {
    if (a[i] == 0) {
      a[i] = -(*st.begin());
      st.erase(st.begin());
    }
  }
  // for (int i = 1; i <= n; i++) {
  //   printf("%d ", a[i]);
  // }
  int l = 1, r = n;
  while (l <= n && a[l] == l) {
    l++;
  }
  while (r >= l && a[r] == r) {
    r--;
  }
  printf("%d\n", r - l + 1);
}

C - Non-Descending Arrays

考试时想到了 dp,但是没写出来!

思路:可以设 \(dp_i,_j(j \in \{0, 1\})\) 为考虑前 \(i\) 个数,换或不换的好集合的数量。

转移一下就好了!

void solve() {
  n = read();
  for (int i = 1; i <= n; i++) a[i] = read(), dp[i][0] = dp[i][1] = 0;
  for (int i = 1; i <= n; i++) b[i] = read(), dp[i][0] = dp[i][1] = 0;
  dp[1][0] = dp[1][1] = 1;
  for (int i = 2; i <= n; i++) {
    if (a[i] >= a[i - 1] && b[i] >= b[i - 1]) dp[i][0] = (dp[i][0] + dp[i - 1][0]) % P;
    if (a[i] >= b[i - 1] && b[i] >= a[i - 1]) dp[i][0] = (dp[i][0] + dp[i - 1][1]) % P;
    if (b[i] >= a[i - 1] && a[i] >= b[i - 1]) dp[i][1] = (dp[i][1] + dp[i - 1][0]) % P;
    if (b[i] >= b[i - 1] && a[i] >= a[i - 1]) dp[i][1] = (dp[i][1] + dp[i - 1][1]) % P;
  }
  printf("%lld\n", (dp[n][0] + dp[n][1]) % P);
}

D - 爆破

考试时一直在想 DSU,考完发现是一个 MST。如果在 m 条边选 \(n - 1\) 条边,就要想到 MST!!!

NOIP 2017 提高组] 奶酪 很像,把距离小于等于 \(h_i + h_j\) 的下标合并。

然后把不同于同一集合的集合建图,跑 Prim 或 Kruskal 即可。

int n, fa[N];
struct Node {
  db x, y, r;
} a[N];
struct TreeNode {
  int x, y;
  db w;
  bool operator < (const TreeNode &A) const {
    return w < A.w;
  }
};
vector <TreeNode> edges;
int find(int x) {
  return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void unite(int x, int y) {
  x = find(x), y = find(y);
  if (x != y) fa[x] = y;
}
db dist(int i, int j) {
  db x = (a[i].x - a[j].x) * (a[i].x - a[j].x);
  db y = (a[i].y - a[j].y) * (a[i].y - a[j].y);
  return sqrt(x + y);
}
void solve() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) fa[i] = i;
  for (int i = 1; i <= n; i++) {
    scanf("%lf%lf%lf", &a[i].x, &a[i].y, &a[i].r);
    for (int j = 1; j < i; j++) {
      if (dist(i, j) <= a[i].r + a[j].r) {
        unite(i, j);
      }
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int j = i + 1; j <= n; j++) {
      if (find(i) != find(j)) {
        db w = max(dist(i, j) - a[i].r - a[j].r, 0.0);
        edges.pb({i, j, w});
      }
    }
  }
  // for (int i = 1; i <= n; i++) fa[i] = i;
  sort(edges.begin(), edges.end());
  db ans = 0;
  for (auto [x, y, w] : edges) {
    x = find(x), y = find(y);
    if (x != y) {
      fa[x] = y;
      ans += w;
    }
  }
  printf("%.2lf\n", ans);
}

E - 燃烧

这是一个记忆化搜索的题目。

如果 \(a_i > a_j\),就可以求出小于子树权值的数量,再加上 \(1\) 即可。

暴力做肯定 TLE,记忆化搜索即可。

int n, a[N];
ll dp[N];
vector <int> edges[N];
inline ll dfs(int u, int fa) {
  if (dp[u] != -1) return dp[u];
  dp[u] = 0;
  ll cnt = 0;
  for (auto v : edges[u]) {
    if (v == fa) continue;
    if (a[u] > a[v]) {
      cnt += dfs(v, u);
    }
  }
  return dp[u] = cnt + 1;
}
void solve() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) scanf("%d", &a[i]), dp[i] = -1;
  for(int i = 1; i < n; i++) {
    int x, y;
    scanf("%d%d", &x, &y);
    edges[x].pb(y);
    edges[y].pb(x);
  }
  ll ans = 0;
  for (int i = 1; i <= n; i++) ans = max(ans, dfs(i, i));
  printf("%lld\n", ans);
}

F - Beautiful Permutation

这是一个交互式问题。

交互题详见 Interactive Problems: Guide for Participants - Codeforces

就是先求出他们的差,再读入两种方式,如果差等于 \([1, n]\) 的差,就可以挪动左指针,否则挪动右指针。

void ask(int op, int j, int &a) {
  cout << op << " " << 1 << " " << j << endl;
  cout.flush();
  fflush(stdout);
  cin >> a;
}
void solve() {
  // fflush(stdout);
  cin >> n;
  int p;
  ask(2, n, p);
  p = p - (n * (n + 1) / 2);
  int l = 0, r = n + 1;
  while (l + 1 < r) {
    int mid = (l + r) / 2;
    ask(1, mid, x);
    ask(2, mid, y);
    if (x == y) 
      l = mid;
    else
      r = mid;
  }
  cout << "! " << l + 1 << " " << l + p << endl;
  cout.flush();
  fflush(stdout);
  cout.flush();
  fflush(stdout);
  cout.flush();
  fflush(stdout);
  cout.flush();
  fflush(stdout);
  cout.flush();
  fflush(stdout);
}

G - Laserphones S

思路:分层图,01 bfs,Dijkstra。

我待会再补。

H - 受欢迎的牛 G

如果缩点后有多个出度,一定无解,否则输出强联通分量的节点数量。

证明:如果答案不在强联通分量中,就一定有一条返祖边可以到达,但是这样就会变成强联通分量,与答案不在强联通分量中矛盾。

void Tarjan(int u) {
  dfn[u] = low[u] = ++idx; 
  stk[++top] = u;
  ins[u] = 1;
  for (auto v : edges[u]) {
    if (!dfn[v]) {
      Tarjan(v);
      low[u] = min(low[u], low[v]);
    }else if (ins[v]){
      low[u] = min(low[u], dfn[v]);
    }
  }
  if (low[u] == dfn[u]) {
    scc++;
    while (1) {
      int v = stk[top--];
      ins[v] = 0;
      bel[v] = scc;
      k[scc].pb(v);
      if (v == u) break; 
    }
  }
}
void solve() {
  n = read(), m = read();
  for (int i = 1; i <= m; i++) {
    int x = read(), y = read();
    edges[x].push_back(y);
    // edges[y].push_back(x);
  }
  for (int i = 1; i <= n; i++) if(!dfn[i]) Tarjan(i);
  vector <int> d(n + 1);
  for (int i = 1; i <= n; i++) {
    for (auto v : edges[i]) {
      if (bel[i] != bel[v]) {
        G[bel[i]].push_back(bel[v]);
        d[bel[i]]++;
      } 
    }
  }
  int ans = 0, cnt = 0;
  for (int i = 1; i <= scc; i++) {
    if (!d[i]) {
      cnt++;
      ans = k[i].size();
    }
  }
  printf("%d\n", cnt == 1 ? ans : 0);
}

总结:要多做一些思维题!拓宽思路!

posted @ 2025-12-30 14:13  AKCoder  阅读(12)  评论(0)    收藏  举报