Atcoder Grand Contest 026 简要题解

本文提供了几道算法竞赛题目的详细解答,包括贪心算法、动态规划等技巧的应用。通过具体的代码实现展示了如何解决ColorfulSlimes、StringColoring等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Colorful Slimes 2

贪心。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  int last = -1, answer = 0;
  for (int i = 0; i < n; ++i) {
    int x;
    scanf("%d", &x);
    if (x == last) {
      ++answer;
      last = -1;
    } else {
      last = x;
    }
  }
  printf("%d\n", answer);
  return 0;
}

rng_10s

先特判 B>AB>AB>DB>D 的情况,否则就是初始有 ABA−B 个罐头,之后每次减去 BB 或者加上 DB 。注意到罐头数量落在区间 [CB+1,CB+D][C−B+1,C−B+D] 内,并且它在模 gcd(B,D)gcd(B,D) 意义下与 AA 同余,那么找最小的那个判断是否大于等于 0 即可。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

bool check(ll a, ll b, ll c, ll d) {
  if (a < b || d < b) {
    return false;
  }
  ll g = __gcd(b, d), t = a % g - g;
  return t < c - b + 1;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int T;
  scanf("%d", &T);
  while (T--) {
    ll a, b, c, d;
    scanf("%lld %lld %lld %lld", &a, &b, &c, &d);
    puts(check(a, b, c, d) ? "Yes" : "No");
  }
  return 0;
}

String Coloring

折半之后随便算算就行了。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  cin >> n;
  string s;
  cin >> s;
  map<pair<string, string>, int> number;
  function<void(int, string, string)> dfs = [&](int x, string red, string blue) {
    if (x == n) {
      ++number[make_pair(red, blue)];
      return;
    }
    dfs(x + 1, red + s[x], blue);
    dfs(x + 1, red, s[x] + blue);
  };
  dfs(0, "", "");
  reverse(s.begin(), s.end());
  ll answer = 0;
  function<void(int, string, string)> rec = [&](int x, string red, string blue) {
    if (x == n) {
      answer += number[make_pair(red, blue)];
      return;
    }
    rec(x + 1, red + s[x], blue);
    rec(x + 1, red, s[x] + blue);
  };
  rec(0, "", "");
  cout << answer << endl;
  return 0;
}

Histogram Coloring

考虑相邻两行(都是满的),它们要么对应位置颜色都相同,要么都不同,相同的还需要满足两行是两种颜色交错。按照高度来DP算方案数就行了。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int mod = 1e9 + 7;

int add(int x, int y) {
  x += y;
  if (x >= mod) {
    x -= mod;
  }
  return x;
}

int sub(int x, int y) {
  x -= y;
  if (x < 0) {
    x += mod;
  }
  return x;
}

int mul(int x, int y) {
  return (ll)x * y % mod;
}

int power(int x, int y) {
  int result = 1;
  for (; y; y >>= 1, x = mul(x, x)) {
    if (y & 1) {
      result = mul(result, x);
    }
  }
  return result;
}

pair<int, int> solve(int l, int r, int base, vector<int> &a) {
  int x = *min_element(a.begin() + l, a.begin() + r), coef = 1, all = 1, number = 0;
  for (int i = l; i < r; ++i) {
    if (a[i] != x) {
      int j = i;
      while (j + 1 < r && a[j + 1] != x) {
        ++j;
      }
      pair<int, int> value = solve(i, j + 1, x, a);
      i = j;
      coef = mul(coef, value.first);
      all = mul(all, add(value.first, value.second));
    } else {
      ++number;
    }
  }
  return make_pair(mul(coef, power(2, x - base)), add(mul(all, power(2, number)), mul(coef, sub(power(2, x - base), 2))));
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
  }
  printf("%d\n", solve(0, n, 0, a).second);
  return 0;
}

Synchronized Subsequence

假设 aai 次出现的位置是 aiaibibi 同理,考虑将序列划分成尽量多段,每一段 a,ba,b 个数相同,那么在同一段中,要么 ai<biai<bi ,要么 ai>biai>bi

  • ai<biai<bi 的情况。答案一定是 abababababab⋯ 的形式,否则有连续的两个 aa ,去掉一个肯定更优。

  • ai>bi 的情况,一定是选择了一个后缀,因为串是 bibi+1aiai+1bi⋯bi+1⋯ai⋯ai+1 的形式,如果 ii 选了 i+1 一定会选,枚举选哪个后缀取最大的。

可以证明,对于每一段,要么不选要么就选最优的串,剩下的做个DP就可以了。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  cin >> n;
  string s;
  cin >> s;
  vector<int> sum(n << 1 | 1, 0);
  for (int i = 0; i < n << 1; ++i) {
    sum[i + 1] = sum[i] + (s[i] == 'a' ? 1 : -1);
  }
  int last = 0;
  vector<string> all;
  function<string(int, int)> get = [&](int l, int r) {
    vector<int> a, b;
    for (int i = l; i < r; ++i) {
      if (s[i] == 'a') {
        a.push_back(i);
      } else {
        b.push_back(i);
      }
    }
    vector<bool> choose(r - l, false);
    if (a[0] < b[0]) {
      int last = -1;
      for (int i = 0; i < r - l >> 1; ++i) {
        if (a[i] > last) {
          choose[a[i] - l] = choose[b[i] - l] = true;
          last = b[i];
        }
      }
      string result = "";
      for (int i = 0; i < r - l; ++i) {
        if (choose[i]) {
          result += s[i + l];
        }
      }
      return result;
    } else {
      string result = "";
      for (int i = (r - l >> 1) - 1; ~i; --i) {
        choose[a[i] - l] = choose[b[i] - l] = true;
        string current = "";
        for (int j = 0; j < r - l; ++j) {
          if (choose[j]) {
            current += s[j + l];
          }
        }
        result = max(result, current);
      }
      return result;
    }
  };
  for (int i = 1; i <= n << 1; ++i) {
    if (!sum[i]) {
      all.push_back(get(last, i));
      last = i;
    }
  }
  vector<string> dp(all.size() + 1, "");
  for (int i = all.size() - 1; ~i; --i) {
    dp[i] = max(dp[i + 1], all[i] + dp[i + 1]);
  }
  cout << dp[0] << endl;
  return 0;
}

Manju Game

将盒子黑白染色,第一个盒子是黑色。记黑色盒子总和是 BB ,白色盒子总和是 W

  • nn 是偶数的情况。不难证明先手可以获得至少 max(B,W) ,而后手至少可以获得 min(B,W)min(B,W)
  • nn 是奇数的情况。如果先手取一个白色盒子,那么后手会从两边中去掉一边变成一个子问题。如果先手拿了黑色盒子,那么先手可以保证 B 的收益而后手可以保证 WW 的收益。

先手决策的过程可以抽象成一棵二叉树:根是先手第一次选的白色盒子,左右儿子分别是两边子问题中先手选的白色盒子。如果先手选黑色盒子,那么这个点不存在。

如果已知先手的决策树,可以发现先手会得到树上所有白色盒子的收益,这些盒子将原序列分成若干段,而后手可以决策让先手在这些段中的其中一段获得所有黑盒子,后手获得这一段中的白盒子。其他段中,黑盒子归后手,白盒子归先手。

考虑二分答案,检验先手能不能获得高于 W+mid 的收益,不难发现就是询问是否存在一种用白盒子来分段的方案,使每一段黑盒子减去白盒子的差都大于 midmid ,这个显然贪心就行了。

#include <bits/stdc++.h>

using namespace std;

bool check(vector<int> &a, int mid) {
  int value = a[0];
  for (int i = 2; i < a.size(); i += 2) {
    if (value >= mid) {
      value = max(value + a[i] - a[i - 1], a[i]);
    } else {
      value += a[i] - a[i - 1];
    }
  }
  return value >= mid;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<int> a(n);
  int sum = 0, black = 0, white = 0;
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
    sum += a[i];
    if (i & 1) {
      white += a[i];
    } else {
      black += a[i];
    }
  }
  if (!(n & 1)) {
    printf("%d %d\n", max(black, white), min(black, white));
  } else {
    int l = 0, r = black, result = 0;
    while (l <= r) {
      int mid = l + r >> 1;
      if (check(a, mid)) {
        result = mid;
        l = mid + 1;
      } else {
        r = mid - 1;
      }
    }
    printf("%d %d\n", white + result, black - result);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值