AtCoder Beginner Contest 172 感想
感想
ABCの三完で惨敗......。ABCで三完なんて久しぶりだなぁ。
解けなかったDは緑diffらしい。めちゃくちゃくやしい。
A - Calc
なので、何も気にすることは無い。
提出コード
void solve() {
ll a;
cin >> a;
cout << a + a * a + a * a * a << "\n";
}
B - Minor Change
となる の個数を数える。
提出コード
void solve() {
string S,T;
cin >> S >> T;
ll cnt = 0;
for (ll i = 0; i < (ll)S.size(); i++) {
if (S[i] != T[i]) {
++cnt;
}
}
cout << cnt << "\n";
}
C - Tsundoku
机Aの本を読む冊数を固定すると、机Bの本をどこまで読めるかは の累積和の上で尺取りか二分探索をすればすぐに求められる。
コンテスト中はまず尺取りで実装しようとしたが、バグらせまくって結局二分探索で解きなおす羽目になった。
「これにぶたんで実装すれば一瞬やなぁ......」とか思いながら尺取りをバグらせ続けたのマジで草(真顔)
提出コード
void solve() {
ll N, M, K;
cin >> N >> M >> K;
Vl A(N), B(M);
for (ll i = 0; i < N; i++) {
cin >> A[i];
}
for (ll i = 0; i < M; i++) {
cin >> B[i];
}
Vl cumA(N + 1), cumB(M + 1);
for (ll i = 0; i < N; i++) {
cumA[i + 1] = cumA[i] + A[i];
}
for (ll i = 0; i < M; i++) {
cumB[i + 1] = cumB[i] + B[i];
}
ll ans = 0;
for (ll i = 0; i <= N; i++) {
if (cumA[i] > K) {
break;
}
ll idx = upper_bound(cumB.begin(), cumB.end(), K - cumA[i]) - cumB.begin();
ans = max(ans, i + idx - 1);
}
cout << ans << "\n";
}
一応尺取りでも解いておいた。
ちなみに実行時間は 二分探索→54ms 尺取り→52ms。logは定数!!!
コンテスト後提出コード(尺取り)
void solve() {
ll N, M, K;
cin >> N >> M >> K;
Vl A(N), B(M);
for (ll i = 0; i < N; i++) {
cin >> A[i];
}
for (ll i = 0; i < M; i++) {
cin >> B[i];
}
Vl cumA(N + 1), cumB(M + 1);
for (ll i = 0; i < N; i++) {
cumA[i + 1] = cumA[i] + A[i];
}
for (ll i = 0; i < M; i++) {
cumB[i + 1] = cumB[i] + B[i];
}
ll ans = 0;
ll idx = M;
for (ll i = 0; i <= N; i++) {
if (cumA[i] > K) break;
while (idx >= 0 && cumA[i] + cumB[idx] > K) {
--idx;
}
ans = max(ans, i + idx);
}
cout << ans << "\n";
}
D - Sum of Divisors
解けませんでした......。
何も思い浮かばず、試しに愚直に素因数分解をして解くコードを書いて手元で試してみるも、当然間に合いそうになく提出を断念......。
結局、エラトステネスの篩っぽく約数を数えれば で済む。C++なら N = 107 でも 解が余裕で間に合う(112 ms)。はやい!!!
また、解説PDFにあるように、等差数列の和を考えることで にできるらしい。
「まとめ方を変えて総和を求める」という典型を思いつくことができず、かなしい。
コンテスト後提出コード
void solve() {
ll N;
cin >> N;
ll ans = 0;
for (ll i = 1; i <= N; i++) {
for (ll j = i; j <= N; j += i) {
ans += j;
}
}
cout << ans << "\n";
}
E - NEQ
Dをあきらめて挑むものの、残り15分程度しか残っておらず解けなかった。以下、解説PDFを見た後の考察。
「 なる任意の について 」を条件1、「 なる任意の について かつ 」を条件2とする。 条件2は要するに数列の中に同じ要素は2つ以上含まれないということ。
以下、条件2を満たす だけを考えて、その集合を普遍集合とする。
を「 である の集合」とする。求める答えは次式のように表される:
とおくと、包除原理より
ここで、解説PDFで述べられている議論より
であり、これを使って を整理すると
を得る。上式を使うと はコンビネーションとパーミュテーションの前計算と合わせて で求められ、解を得ることができる。
コンテスト後提出コード
// ModInt, ModComb 略
constexpr ll MOD = 1000000007;
using Mint = ModInt<MOD>;
void solve() {
ll N, M;
cin >> N >> M;
ModComb<MOD> mc(M);
Mint ans = mc.perm(M, N).pow(2);
for (ll i = 1; i <= N; i++) {
Mint t = mc.comb(N, i) * mc.perm(M, i) * mc.perm(M - i, N - i).pow(2);
if (i & 1) {
ans -= t;
}
else {
ans += t;
}
}
cout << ans << "\n";
}
F - Unfair Nim
問題を見てすらいないので割愛。