Toy と帽子と ADP BE

主にプログラミングに関わる話をゆるくエモくやっていきます

C. And Matching (Codeforces Round #768 Div. 2)

問題

https://codeforces.com/contest/1631/problem/C

問題概要

2の累乗である整数nと0 <= k <= n - 1である整数kが与えられる。

n/2個のペアを以下の条件を満たすように作れ。

  • 各ペアには0からn-1までの整数が一つずつ含まれている
  • ペア毎にbitwise ANDを取り、それらの数の和がkになるようにせよ
    • より形式的には、i番目のペアの2つの数をa_i, b_iとしたとき、 \displaystyle \sum_{i=1}^{n/2} a_i AND b_i = kを満たすようにせよ

考察

問題の制約より、0以上n-1以下の数についてのみ考えます。

また、nは2の累乗なのでn-1はすべてのビットが立っている数です。n=8ならn-1 = 7_{(10)} = 111_{(2)}ですし、n=16ならn-1 = 15_{(10)} = 1111_{(2)}です。

k = 0のとき

x AND (x XOR (n - 1))は0となります。x=0のとき(x XOR (n - 1))=n-1x=1のとき(x XOR (n - 1))=n-2、...、x=n/2-1のとき(x XOR (n - 1))=n/2となるので、0とn-1、1とn-2、2とn-3...、というペアを作っていけばすべて0になり、和ももちろん0になります。

k < n - 1のとき

n-1 XOR x = xが成り立ちます。

また、前述のk = 0のときの解法では、n-1は0とペアになっておりkは(k XOR (n - 1))とペアになっています。ここでn-1とkをペアにして0と(k XOR (n - 1))をペアにすれば、前者でkが作れ後者で0が作れて、その他のペアは変わらず0のままなので最終的な和はkとなります。

k = n - 1のとき

n=4のときは、サンプルにあるとおり構築不可能です。以下n>=8のときで考えます。

上記の解法ではn-1を一発で作ることはできません、n-1とn-1をペアにしないとn-1は作れませんが、n-1はもちろん一度しか使えないためです。

そこで、一旦n-1とn-2をペアにします。これでn-2が作れて、あとは1を作ればよいとなります。とりあえず1 AND 3 = 1なので、それで考えてみます。

この時点で使っていない数は0, 24, 5, 6, ..., n-3です。これらについて、k = 0のときの解法と同じロジックで0を作れるペアをすべて作ると0とn-4が残ります。0が残っているのでn-4がいくつであるかにかかわらずペアにすると0です。よってk=n-1も達成できました。

#include <bits/stdc++.h>
using namespace std;
 
int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, k;
        cin >> n >> k;
        if (n == 4 and k == 3) {
            cout << -1 << endl;
            continue;
        } else if (n - 1 == k) {
            cout << 0 << " " << (n - 4) << endl;
            cout << 1 << " " << 3 << endl;
            cout << (n - 2) << " " << (n - 1) << endl;
            for (int i = 0; i < n / 2; i++) {
                if (i == 0 or i == 1 or i == 3) continue;
                cout << i << " " << (i ^ (n - 1)) << endl;
            }
        } else {
            cout << k << " " << n - 1 << endl;
            for (int i = 1; i < n / 2; i++) {
                int r = i ^ (n - 1);
                if (i == k) {
                    cout << 0 << " " << r << endl;
                } else if (r == k) {
                    cout << 0 << " " << i << endl;
                } else {
                    cout << i << " " << r << endl;
                }
            }
        }
    }
    return 0;
}

AtCoder Beginner Contest 236

3完。いやー・・・。

各問題

A - chukodai

swap(S[a-1], S[b-1])してからSを出力します。

B - Who is missing?

サイズN(+1)の配列を用意して出てきた数字を入れていき、4になってないところが答えです。

配列のサイズをNにして出てきた数字を1引くやり方もありますが、配列のサイズをN+1にする方が事故が少なそうです。その場合0番目を考慮に入れないことにだけ注意です。

C - Route Map

Tは今どこまで一致したかを保存しておき、Sの方をループで回して一致すればTのポインタを一つ進めていけばよいです。

D - Dance

ビット全探索からのnext_permutationで全探索可能かと思いきや、手元で2秒前後までしか詰められずジャッジは無情にもTLE・・・。

最後はひたすら定数倍削減してみましたが及ばず・・・。

全探索の実装方法がそもそも間違っている?(追記:間違ってました。公式解説はもうちょっと単純な方法でした。)

まとめ

Cまでがとても早く解けたにもかかわらずDに引っかかってしまい無念の緑パフォ・・・。

f:id:mdstoy:20220123225842p:plain

AtCoder Regular Contest 133

2完。

各問題

A - Erase by Value

辞書順最小といういことで、前に大きい数字があったら損ですから、前から順番に単調減少しているところを探していって、見つかったらそれの大きい方の数を抜けばよいです。

広義単調増加列だった場合は一番うしろの数を抜きましょう。

B - Dividing Subsequence

LISっぽいことをすればよいというのはわかったのですが、そもそもLISがちゃんと理解できていないというあれ・・・。

というわけで最後までTLE解しか書けず。

C - Row Column Sums

ありえる最大の数は、(K-1) * H * Wから、(K-1) * W % KとAとの差分(行について)と(K-1) * H % KとBとの差分(列について)の大きい方を引いたものになります。

また解が存在するかどうかは、上記の行についての差分のmod Kと列についての差分のmod Kが一致するかどうかでわかります。

というわけで、この問題が解けました。

とかいって、実戦中は全く証明せずに勘で投げました・・・。

まとめ

Bで粘りすぎてからのCが10分で通せたのはパフォ的にもったいなかったですが、まあCもちゃんと証明できたわけじゃないのでむしろもうかったといえるかも。

レートは一応連続でHighest更新です。

f:id:mdstoy:20220122233235p:plain

HHKB プログラミングコンテスト 2022(AtCoder Beginner Contest 235)

5完1WA。

各問題

A - Rotate

文字をばらして並べ替えて数値に変換して足す、をしました。

こういう問題はどうとでもできるっちゃあできるんですけど、どうやるのが一番早いのか未だによくわからず・・・。今回も無駄に3分以上かけてます。つらい。

(追記)

abc + bca + cab は、a*100 + b*10 + c + b*100 + c*10 + a + c*100 + a*10 + b = a*111 + b*111 + c*111 = (a+b+c)*111 となるそうで。いや言われてみればそれはそうすぎる・・・。

B - Climbing Takahashi

単純に現在位置と右隣と比較して、移動できたらして、移動できなくなったらそこが答えです。右端の処理は、0を余分にくっつけるでもよし、ループが最後まで行ききったら最後のHが答えだとしてもよし。

C - The Kth Time Query

map<int, vector<int>>で、各a毎に何番目に登場するかを保存しておけば、m[x]のサイズがkより小さければ-1だし、大きければmx[x][k - 1]を答えればよいとなります。

D - Multiply and Rotate

数値変換の最中に同じ数字を経由するのは意味がなく、桁数が減ることもありえないので、最悪でも106回で処理が終わります。よってメモ化再帰を書けばよいだけです。

E - MST + 1

最小全域木なのでクラスカル法をすればよいのですが、Gに含まれる辺もクエリで出てくる辺もすべて含めてソートしてしまってから、クラスカル法をします。

採用された辺がGに含まれる辺なら (Union-Find Treeに) merge、クエリに含まれる辺なら"Yes"を出力、採用されなかった辺がGに含まれる辺なら無視、クエリに含まれる辺なら"No"を出力、とすればよいです。

それ以降

なんもわからん・・・。

まとめ

10ヶ月ぶりのHighest更新となりました!!

しかし青パフォは遠い・・・。Eの解法をすぐに思いつけるようにならないとだめという領域に来てしまったのかー。

f:id:mdstoy:20220115233458p:plain

AtCoder Beginner Contest 234

5完。

各問題

A - Weird Function

問題文通りに関数を定義して、問題文通りに呼び出せばよいです。

B - Longest Segment

全ての点の組み合わせの距離を出して一番長いものを取ればよいです。

C - Happy New Year!

Kを2進数表記して、1を2に変えればよいです。

これ、たまたま気づいちゃったって感じだったんですけど、考察ってどうやってするんでしょ?(おい

D - Prefix K-th Max

priority_queueに、それまでに登場したうちで上位K個を(小さいものが上になるように)保存しておき、次のPがqueueの中の最小の数より小さいなら無視、大きいならそれまでの最小を捨ててPを加えたうえで、最小の数が何かを確認します。

E - Arithmetic Number

これ、上二桁が決まった時点で後ろに続く数は一意に定まるので、等差数の数は相当少ないです。なので、等差数を先に列挙してしまえば後は二分探索で見つかります。

自分は実戦では(等差数が少ないことを考慮した上で)再帰を書いて求めました。

F - Reordering

DPなのはわかるのですが、並び替えがわからんとなってました。

まとめ

Eまで詰まらずに解けたのはよいですが、Fとしては比較的簡単そうに見えるDPが解けなかったのはいかんですね。

f:id:mdstoy:20220108232035p:plain

AtCoder Regular Contest 132

2完3WA。

各問題

A - Permutation Grid

Rの大きい行の黒マスを確定→Cの小さい列の白マスを確定、としていくと一意に定まり、黒く塗られるマスはR_i + C_j がnより大きいマスです。

なので、各クエリ毎にr + c > n なら黒で、そうでないなら白です。

B - Shift and Reverse

どのような順番で操作をしようとも、並びが「ぐちゃぐちゃ」になることはありません。(いや表現・・・)

なので、1とNの位置関係で操作の回数は一意に求まります。

C - Almost Sorted

DPで解くのだろうとはわかったのですが、すでに使った数字をどう管理してよいのかわからず・・・。

まとめ

せっかくAを5分で解いたのに、Bがすぐに見えなかったために緑パフォでHighestはお預けとなりました。

まあ秋頃の大スランプを思えば、1300台で年が越せるのは十分といえます。来年は水色後半くらいは行ってみたいですねー。

f:id:mdstoy:20211226231156p:plain

AtCoder Beginner Contest 233

5完1WA。

各問題

A - 10yen Stamp

算数の問題を、ときます。すでにXがYを超えているケースに注意する必要があります。

B - A Reverse

Lまでと、LからRのあいだと、R以降に分割して、真ん中のやつをreverseしてからくっつけ直せばよいです。

なぜか添字をミスりまくり、5分かけてしまいました・・・。

C - Product

「袋に入っているボールの個数の総積は 105を超えない。」という条件があるため、総当りしても間に合います。

D - Count Interval

累積和を取ります。あるsum[i]に対してsum[i] - Kとなるようなsumjがあれば、それが求める区間になります。

なので、iを順番に見ていって条件を満たすsum[j]を探せばいいのですが、愚直に見ていくと間に合わないので、すでに見終わった場所についてどの値がいくつ出現したかをmapで管理すれば1つのiにつきO(1)、全体でO(N)で(mapを使うのでO(NlogN)で)解くことができます。

デバッグ出力を消し忘れて1WA・・・。(実はこれが悲劇を招いた)

E - Σ[k=0..10100]floor(X/10k)

一番上の位には1番目の数のみが足され、2番目の位には1番目と2番目の数が足され、となるので、各桁の数値の累積和を取って、一番下の位から順に繰り上がりを考慮しつつ足し上げていけばよいです。

F - Swap and Sort

1から順にダイクストラで最短経路を出してシミュレートしていくという(いかにも嘘っぽい)貪欲解を書きましたが、案の定WAでした。

まとめ

順調にレートが伸びてHighest - 1まで上がってきましたが、D問題のデバッグ出力消し忘れがなければHighest更新でした・・・。もったいなーい。

水パフォ以上Streakも7で自己ベスト更新中です。いい感じ。青まではなかなか行かないのが微妙ではありますが。

f:id:mdstoy:20211225230515p:plain