Atcoder Grand Contest 026 简要题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wxh010910/article/details/81479560

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 > A B > D 的情况,否则就是初始有 A B 个罐头,之后每次减去 B 或者加上 D B 。注意到罐头数量落在区间 [ C B + 1 , C B + D ] 内,并且它在模 gcd ( B , D ) 意义下与 A 同余,那么找最小的那个判断是否大于等于 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

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

  • a i < b i 的情况。答案一定是 a b a b a b 的形式,否则有连续的两个 a ,去掉一个肯定更优。

  • a i > b i 的情况,一定是选择了一个后缀,因为串是 b i b i + 1 a i a i + 1 的形式,如果 i 选了 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

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

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

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

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

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

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

猜你喜欢

转载自blog.csdn.net/wxh010910/article/details/81479560