【PGBattle2023】おせんべい1-3

https://products.sint.co.jp/pg_battle

3完80点で19分くらいでした。せんべい4問目を5分くらい考えてギブアップ。昨年より、かなり簡単だったように思う。
[お知らせ] どうも問題4のテストケースに不備があったようで、4問目の正誤とせんべいの回答時間が審査から除外されるようです。

Q1. せんべい(難易度2)積の符号
N個の数を全部かけたときに、+か-か0か教えて。
N <= 200000

考察
0があったら0だし、負の数が奇数個あったら-。

実装

int main() {
  ll N;
  cin >> N;
  ll m = 0, p = 0, zero = 0;
  rep(i, N) {
    ll a;
    cin >> a;
    if (a < 0)
      ++m;
    else if (a > 0)
      ++p;
    else
      ++zero;
  }

  if (zero > 0) {
    cout << 0 << endl;
  } else {
    if (m % 2 == 0) {
      cout << '+' << endl;
    } else {
      cout << '-' << endl;
    }
  }
}



Q2. せんべい(難易度3)ABCの個数
A,B,C,?で構成された文字列Sが渡される。
?にはABCを任意でいれられるとき、部分文字列"ABC"の個数の期待値を教えて。
|S| <= 200000

?BC

0.333333333334

考察
DP[3][200000] // ABCの文字列, index
で管理。indexを1文字ずつ処理すればよさそう。
答えは、'C'が来た時に、前の文字が'B'かつ、前の前の文字が’A’の期待値を足したものになりそう。
Bがきたときに、前の文字がAの期待値を足しておけばいい。

実装

ld DP[3][200020];  // ABC, ex

int main() {
  cincout();

  string S;
  ll N;
  cin >> S;
  N = S.size();

  ld ans = 0.0;

  rep(i, N) {
    if (S[i] == 'A') DP[0][i] = 1.0;
    if (S[i] == 'B' && i > 0) DP[1][i] += DP[0][i - 1];
    if (S[i] == 'C' && i > 1) DP[2][i] += DP[1][i - 1];
    if (S[i] == '?') {
      DP[0][i] = 1.0 / 3.0;
      if (i > 0) {
        DP[1][i] = DP[0][i - 1] / 3.0;
        DP[2][i] = DP[1][i - 1] / 3.0;
      }
    }
    ans += DP[2][i];
  }
  cout << ans << endl;
}

Q3. せんべい(難易度4)距離 K
N個の整数の数列Aがある。K離れた要素(index iとi+K)を任意にswapできるとき、数列のバリエーションを998244353で割ったあまりで教えて。

10 2
3 1 4 1 5 9 2 6 5 3

3600

考察
自由に動かせるバリエーションがいくつあるか考える。
K=2のときは,index{0,2,4,6,…}と{1,3,5,7}のuniteができる。
K=3のときは3つのuniteができる。
それぞれについて要素数の階上通りのバリエーションがある。

ところが、同じ数字を入れ替えてもバリエーションが増えないので、数字が同じ数だけ、階上通りのバリエーションを除く。

実装

int main() {
  MOD = 998244353;
  COMinit(); // 2項定理のライブラリ。inv[], finv[]の生成
  ll N, K;
  cin >> N >> K;
  vector<ll> A(N);
  for (auto &a : A) cin >> a;
  if (K == 0) { // K = 0のときは変化しないので1パターン
    cout << 1 << endl;
    return 0;
  }

  mint ans = 1;

  rep(k, K) {
    vector<ll> V;
    ll cur = k;
    while (cur < N) {
      V.emplace_back(A[cur]);
      cur += K;
    }
    sort(all(V));
    ans *= fac[V.size()];
    // 同じ値の数だけ階上で割る
    ll cnt = 1;
    rep(i, V.size() - 1) {
      if (V[i] == V[i + 1])
        cnt++;
      else {
        ans *= finv[cnt];
        cnt = 1;
      }
    }
    ans *= finv[cnt];
  }
  cout << ans << endl;
}

Q4. せんべい(難易度6)トリオ
1,2,...,3Nの人がいる。3人チームのコンテストを2回開催する。
1回目は、{1,2,3},{4,5,6},...でメンバーを組む。

2回目は、それぞれ違うメンバーを組む。このとき何通りあるか。
ex N=3のとき、
{(1,4,7), (2,5,8), (3,6,9)}はok
(1,2,4)は1,2が同じメンバーのため不可

答えはMで割った余りを求めて。
1 <= N <= 300
1e8 <= M <= 1e9

考察
N=300はDPだよね。わからない。

この記事が気に入ったらサポートをしてみませんか?