Codeforces Round 960 (Div. 2)

Luckyblock發表於2024-07-21

目錄
  • 寫在前面
  • A
  • B
  • C
  • D
  • E
  • 寫在最後

寫在前面

比賽地址:https://codeforces.com/contest/1990

依舊是紅溫溫溫溫溫溫溫溫溫溫場。

AB 每題都吃了兩發,爽!D 到最後也沒調出來賽後五分鐘看出來哪掛了,爽!又要掉一百分了,爽!

所有錯誤都是每題多看兩眼就能找到的但就是沒多看兩眼。

媽的傻逼一天天的在這幹啥啊、、、急成這個逼樣子還是急著投胎算了。

A

手玩。

操作等價於選擇某個權值的第一個數,並將在此之前所有數全部刪除。

先考慮只有一種權值的情況。顯然此時若 \(n\) 為奇數,則先手必勝,否則先手必敗。

然後考慮多種權值。發現若所有權值的出現次數均為偶數,則先手必敗,否則先手僅需選擇最大的出現次數為奇數的權值,即可令後手進入必敗狀態。

於是僅需檢查是否有一種權值出現次數為奇數即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    int n; std::cin >> n;
    int cnt[100] = {0}; 
    for (int i = 1; i <= n; ++ i) {
      int a; std::cin >> a;
      ++ cnt[a];
    }

    int flag = 0;
    for (int i = n; i; -- i) {
      if (cnt[i] % 2 == 1) flag = 1;
    }
    std::cout << (flag ? "YES\n" : "NO\n");
  }
  return 0;
}

B

構造。

顯然應令 \([y, x]\) 內全部為 1,\(y - 1, x + 1\) 均為 -1。然後考慮令 \([1, y - 1]\)\([x + 1, n]\) 的前字尾和均儘可能地小以符合題意。

發現僅需從 \(y - 1, x + 1\) 開始,分別向左右構造成 -1/1 交替的形式即可。此時 \([1, y - 1]\)\([x + 1, n]\) 的任意前字尾和均不大於 1。且若 \([1, y - 1]\) 存在字首和為 1 則 \([1, y-1]\) 和為 0;若 \([x+1, n]\) 存在字尾和為 1 則 \([x+1, n]\) 和為 0。而在 \([y, x]\) 內至少會獲得 +2 的貢獻,則上述構造一定合法。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int n, x, y, ans[kN];
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n >> x >> y;
    // int a = x - 1, b = y - x - 1, c = n - x;
    ans[x] = ans[y] = 1;
    for (int i = y; i <= x; ++ i) ans[i] = 1;
    for (int i = y - 1, j = -1; i; -- i, j = -j) ans[i] = j;
    for (int i = x + 1, j = -1; i <= n; ++ i, j = -j) ans[i] = j;

    for (int i = 1; i <= n; ++ i) std::cout << ans[i] << " ";
    std::cout << "\n";
  }
  return 0;
}

C

模擬,暴力。

先手玩一下。前兩次操作沒什麼規律,但發現從第二次操作開始,數列一定為單調遞增,且除了最大的權值外每種權值至少出現 2 次,且每次操作對數列的影響均為使數列整體右移一位,刪去最後一個數並在前面補 0。

於是先暴力模擬兩輪記錄貢獻和,然後求得每種權值的出現次數,再降序列舉每種權值(也即每輪刪數的順序),統計刪去該權值的各輪的貢獻之和即可。

總時間複雜度 \(O(n)\) 級別。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, a[kN], cnt[kN];
LL ans;
//=============================================================
void solve() {
  for (int i = 0; i <= n; ++ i) cnt[i] = 0;

  int mad = 0;
  for (int i = 1; i <= n; ++ i) {
    ans += a[i];

    ++ cnt[a[i]];
    if (cnt[a[i]] >= 2 && a[i] > mad) mad = a[i];
    a[i] = mad;
  }
}

LL sum(int L_, int R_) {
  return 1ll * (L_ + R_) * (R_ - L_ + 1ll) / 2ll;
}
void solve1() {
  LL delta = 0;
  for (int i = 0; i <= n; ++ i) cnt[i] = 0;
  for (int i = 1; i <= n; ++ i) delta += a[i], ++ cnt[a[i]];

  for (int i = n; i; -- i) {
    if (cnt[i] == 0) continue;
    ans += 1ll * cnt[i] * delta - sum(1, cnt[i] - 1) * i;
    delta -= 1ll * cnt[i] * i;
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n;
    ans = cnt[0] = 0;
    for (int i = 1; i <= n; ++ i) {
      std::cin >> a[i];
      cnt[i] = 0;
    }
    solve();
    solve();
    solve1();
    std::cout << ans << "\n";
  }
  return 0;
}
/*
1
3
2 2 3
*/

D

手玩,貪心。

發現使用 \(2\times 2\) 矩陣的操作 1 大部分情況下是不值的,對某兩行使用兩次操作 1 一定不優於兩次操作 2。

手玩下可以發現如下結論:

  1. 使用一次操作 2 等於對下方所有行消除之前進行的操作 1 的影響,於是僅需考慮每次連續使用操作 1 的影響。
  2. 使用操作 1 被覆蓋的行黑色數量一定不會大於 4,否則不優。
  3. 連續使用操作 1 時,若使用了 \(k\) 次則一定會使 \(k+1\) 行被全部覆蓋,否則不優。
  4. 連續使用操作 1 時,被覆蓋的第一行黑色數量一定不會大於 2,否則不優。
  5. 由結論 1234,連續使用操作 1 時,第一次操作 1 覆蓋的範圍一定是 \((x, 1), (x, 2)\),第二次一定是 \((x+1, 3), (x+1, 4)\),第三次一定是 \((x+2, 1), (x+2, 2)\),……直至第 \(x+k\) 行黑色數量大於 4,或使用操作 1 後無法覆蓋到第 \(x+k+1\) 行,此時對第 \(x+k\) 行進行一次操作 2 一定不會更劣。然後從第 \(x+k+1\) 行開始重複上述過程。

根據結論 45,僅需從第一行開始貪心地操作即可。若某行滿足進行第一次操作 1 的條件,則連續進行操作 1 直至不滿足條件,再換用一次操作 2,再從下一行開始重複上述過程。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, a[kN];
//=============================================================
int solve(int now_, int left_, int right_) {
  if (now_ > n) return 0;

  if (a[now_] <= 0) return solve(now_ + 1, 0, 0); //此行不需操作
  if (a[now_] <= 2) {
    if (left_) return solve(now_ + 1, 0, 0); //左側兩個已被覆蓋,則不需操作此行
    return solve(now_ + 1, 1, 0) + 1; //對此行進行操作 1,不會更劣
  }
  if (a[now_] <= 4) {
    if (right_) return solve(now_ + 1, 1, 0) + 1; //右側兩個已被覆蓋,對左側操作
    if (left_) return solve(now_ + 1, 0, 1) + 1; //左側兩個已被覆蓋,對右側操作
    //若無覆蓋,對此行操作 2,不會更劣。
  }
  return solve(now_ + 1, 0, 0) + 1; //此行操作 2
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n;
    for (int i = 1; i <= n; ++ i) std::cin >> a[i];
    std::cout << solve(1, 0, 0) << "\n";
  }
  return 0;
}
/*
1
4
3 2 1 0

1
4
2 4 4 2

1
3
2 3 5

1
6
2 4 4 4 4 2
*/

E

互動,

寫在最後

學到了什麼:

  • 急你馬呢
  • 急你馬呢急你馬呢
  • 急你馬呢急你馬呢急你馬呢
  • 急你馬呢急你馬呢急你馬呢急你馬呢
  • 急你馬呢急你馬呢急你馬呢急你馬呢急你馬呢
  • 急你馬呢急你馬呢急你馬呢急你馬呢急你馬呢急你馬呢
  • 急你馬呢急你馬呢急你馬呢急你馬呢急你馬呢急你馬呢急你馬呢

我操不想活了。

相關文章