Hello Barcelona Day4
Day3はLink-Cut木コンテストだったんですが、むずすぎたのでもうちょっと理解してから記事にしようと思います。 Day4はstereometry(空間の幾何?)コンテスト
A - Elementary
問題
4点与えられるのでそれらを頂点とする四面体の体積を求めて、という問題
解法
三重積を計算して6で割るだけ
B - Cross Spider
問題
点が与えられるので同一平面上にあるか判定せよ。XYZ座標はそれぞれ絶対値が1000000以下。
解法
座標が大きく、内積や外積の演算をするとdoubleでは誤差が心配(ギリギリいけそうだけど)なのでlong longで計算する。 平面の法線ベクトルが確定したらあとはそれと直交するかをみればいいだけ。
C - Line Teleportation
@kenshinが知らない間に通してた。
D - Segment Distance in 3D
この前まとめました。
E - 3D Printing
問題
30面程度の凸な多面体が与えられるので体積を求めよ、という問題
解法
@oginginが通してくれた。
H - Connected Rings
リングが2つ与えられるので、鎖状につながっているか判定せよ。リング同士が交点をもつことはない。
解法
@kenshinが気合で座標計算し、通してくれた。プロすぎる。
感想
この日は自明問題しか貢献できなくて厳しい気持ちになった。
Hello Barcelona Day2
Day1の内容はJAG合宿Day3と全く同じだったようです。なので記事はDay2からにしようと思います。
A - Alice and Maze
問題
頂点辺の無向グラフが与えられる。各頂点に報酬、各辺にコストが設定されている。 頂点を訪れるたび、その報酬が貰え、辺を通るには、そのコストを支払わなければならない。 スタートは頂点1でゴールは頂点とする。Aliceはスタート時点で円持っており、お金を最大化した後 ゴールから出たい。スタートとゴールの頂点を含め、同じ頂点や辺は複数回通ってもよい。 そもそもゴールにたどり着けない場合は-2, いくらでもお金を増やせる場合は-1, 有限のお金まで増やせる場合は、その金額を出力せよ。
制約
- 4秒
- 10テストケース
- 初期のお金、報酬、コストはそれぞれ1円以上円以下
解法
問題は無向グラフで与えられているが、進む方向によって実質かかる費用は異なるので、辺数を2倍にした有向グラフで考える。 ベルマンフォードをやるだけだが、更新時に、現在の所持金が辺のコスト以上かどうかをチェックし、その場合のみ更新するようにする。
さらに無限大に発散するループが存在する場合、(頂点数)回目のループで更新された頂点を列挙し、これらのうち1つ以上がゴールから到達可能な場合、 いくらでも所持金を多くしてゴールに到達可能であることがわかる。
計算量はベルマンフォードは本質で かなりギリギリ(テストケースを考慮するとオーダーだし)だけど想定解だった。 最初、無限ループ検出のときに、回目のループだけではなく、から回目までのループで更新された頂点を全列挙していたが、この場合TLEした。 @oginginがそれ回目だけでよくね?と言ってくれてプロだった。
B - Buggy Stack Machine
問題
PUSH, POP, ADD, EMPTYの命令(ADDは、上2つPOPし、それらをADDしたものをPUSHする命令)があるスタックがあるが、上からk個の数の2進数での1の位を並べたものが となった瞬間、これらk個の数がすべて消えてしまう、というバグを持っている。 クエリが与えられるので、それぞれのPOPクエリに対して、POPされる値を答えよ。しかし、それまでに不可能な操作(たとえば空のスタックに対するPOPや、要素が1以下のスタックに対するADD)が呼ばれた場合には、ERRORを答えよ。
制約
- 5秒
- 50テストケース
- クエリの数は100000以下
- 入力文字列は60 mebibytes以下
解法
@kenshinが最初これを担当していて、bitsetを使って1の位を覚えておくのは間に合う?と聞いてきた。見積もってみるとかなりギリギリだったけど、まあいけそうかなと言って実装する。
C++の標準のbitsetでは、大きさが異なるbitset同士を比較できないのでかなり困った。大きい方のbitsetに合わせてコンストラクトし直して比較するとTLEするので(定数倍で10倍かかる)、
頑張って比較を実装する。@kenshinが終了間際にWAで苦しんでいたのでソースコードを読んでみると、if (s.size() - (9999 - j) >= 0)
というヤバそうな部分を見つける。intにキャストしたらACした。残り4分だった。
感想
さすがに想定解じゃなかった。他チームでもbitsetで書いたところが多かったみたいだが、ほとんどがTLEしてたらしい。 想定解はKMP法だったが、チームメイトは誰もKMP法を実装したことがなかったのでかなり厳しかった。
C - Constellation
読んでない。@oginginが知らない間に通してた。
D - Domination
読んでない。 @kenshinが知らない間に通してた。
E - Elevator
問題
202階のビルがあってエレベーターに人が1階から乗る。それぞれの人には行きたい階があるが、ちょうどその階に止まらなくても、1階分だけなら階段を使って下に降りることができる。 エレベーターは1階を除いて最小でいくつの階に止まるか。
制約
- 1秒
- 100テストケース
解法
貪欲にやるだけ。202階が謎制約すぎる。
G - Good Substring
問題
ある文字列がgoodであるとは、そのいかなる部分文字列も長さ2以上の回文を含まないことである。文字列と、クエリ(番目の文字をに変えるというもの)が与えられるので、各クエリ後の 最長のgoodな部分文字列の長さを答えよ。
制約
- 2秒
- 1テストケース
- 文字列の長さ、クエリの数ともに以下
解法
ある文字列が部分文字列に回文を含む必要十分条件はaaもしくはaba型の部分文字列を含むこと(お な じ み)なので、クエリのたびに、この位置を更新し、これらの位置の間隔で最長のものを答えればいい。 問題を言われてhoge秒で解法がわかったが、コンテスト中に読んでなかったので通せなかった。本当にもったいない
蟻本リーディング 組み合わせ編
飛行機の移動が暇なので蟻本を読んでまとめることにしました。
今回は組み合わせ問題編です。
分割数
問題
個の互いに区別できない品物を個以下に分割する方法の場合の数をで割ったあまりを求めよ。 たとえば、4つの品物を(1, 1, 2)と分割するのと(1, 2, 1)と分割するのは同じ分割だと数えます。
解法
mod素数の場合、combinationがで求められることにより、事前計算も込みでで解くことができます。 modが素数でない場合は、コンビネーション計算時に逆元が求まらないので、DPをします。
番目までの品物をちょうど個に分割する分割数とします。
ちょうど個に分割する方法は、番目までの品物が個に分割されていて番目の品物を別の1グループとする場合と、 番目までの品物がグループに分割されていて、i番目の品物をどれかに追加する、という場合になります。
したがって、となります。 こうしてでこの問題が解けました。
重複組合せ
種類の品物があり、番目の品物は個あります。異なる種類の品物は区別できますが、同じ種類の品物は区別できません。これらの品物の中から個選ぶ組み合わせの総数をで割った値を答えなさい。
解法
種類目までから個選ぶ組み合わせというDPを考えます。この数え上げを、種類目のものを選ぶ場合とそうでない場合に分けて考えます。 種類目のものを選ばない場合は通りですね。種類目のものを1つ以上選ぶ場合、通りです。これは累積和を同時に求めながら計算するとで計算できます(蟻本にはp.67にはこれを上手く変形して累積和なしで計算する方法も載ってあります)。
D - Factorization
D - Factorization
長さの正整数の配列で、各要素の積が正整数となるようなものが何通りなるか、という問題。
解法
の素因数分解を考え、とします。今の問題は次のように言い換えることができます。
個の箱があり、個のを重複を許して入れます。 それぞれのについてこの作業をする場合の、入れ方の場合の数はいくらか。
この言い換えが問題の本質です。たとえばとしたときに[1,1,8]も[1,2,4]も[2,2,2]も全部今の方法でもれなく数え上げられていることを確認してみてください。
このときの場合の数は、について独立に考えることができます。そこで以下の問題を考えます。
個の箱があり、個のを重複を許して入れる場合の数はいくらか。
これは重複組合せの計算であり、番目までのアイテムから個重複を許して入れる場合の数、とした動的計画法を考えることで解けます。
F. The Shortest Statement
F - The Shortest Statement
問題概要
頂点辺の連結な重み付き無向グラフが与えられる。個のクエリに答えよ。番目のクエリでは頂点間の最短距離を答えよ。
考えたこと
木っぽいグラフが与えられるということなので、とりあえず木に場合について考えてみることにする。 木の場合はまんまLCAを使うことができます。具体的には
- 適当な1頂点を根として、根付き木を考える。この根から各頂点への最短距離をbfsでもして求めておく。
- に対して、これらのLCAをで求め、それをとおく。
- 間の最短距離は、である。
次はこれをいまのグラフに拡張してみましょう。 グラフから適切に辺を本削除すると木になります。 削除された辺につながっている頂点の集合をとします。
元のグラフ上での2頂点間の最短経路はもちろん削除された辺を通る場合があります。そこで、削除された辺も含めたの 中の任意の2頂点間の距離をワーシャルフロイドで事前計算しておきます。
このとき、間の最短距離は次のように計算することができます。
- 最短距離を木上での最短距離で初期化します。
- から木上を通ってまでいき、そこからに抜けて、 から木上を通ってに行く経路をすべてのの組み合わせについて計算し、最短距離を更新する
定数倍が重めですが、でこの問題を解くことができました。
Codeforces Round #512
CでWAを引きすぎて飛ばしてしまい、Dから解いたので574位になってしまいました、、、
C - Vasya and Golden Ticket
0を除いて考えても同じなのではじめに0を除去しておきます。除去後の文字列が空列なら、これは"0...0"という入力に対する場合であり、"YES"を返します。
そうでない場合、番目の桁までの桁和(累積和)をとり、とします。1ブロックの桁和としてありえるのは、 なので、それぞれに対し可能かどうか判定していきます。なお、ここでの文字列の長さ、はトリミング後の長さとします。
可能かどうかの判定は左から貪欲に見ていけばいいです。事前に0を削除しておかないと、貪欲で判定していくときに、可能な最長の部分列を見たり、特別処理をしたりする必要が出てきて多少バグらせやすくなると思います。
たとえば、"1100000"は、明らかに可能ですが、"1" + "1" + "00..."に分けようとするとダメで、"1" + "100..."に分けないとだめです。もしくは最後に"00.."が続いた場合は特別処理するとかしないとダメですね。
D - Vasya and Triangle
1以上の整数と整数が与えられるので、長方形に収まっているような 格子点3点からなる三角形で面積がとなるものを一つ構成せよ、という問題
考えたことと解法
格子点からなる三角形の面積が0.5刻みであることは知っていたので、とりあえず、が半整数じゃない場合は"NO"を 返して、半整数の場合にどうなるかを考えました。
いくつか試してみると、作れるなあという気分になって構成を試みます(後述しますが、長方形内に収まる三角形の面積は、実は0.5からまでの0.5刻みのものがすべて構築可能です。)。
面積が半整数となる場合を考えましょう。このときは整数です。
うまく約分するとこれが整数になることから、となるような1以上の整数が存在し、 これらを使って2整数を またはとなるように作ることができます。このどちらかは、がそれぞれ以下になっているはずですなので)。 よって3点を選べば、面積がの面積が得られます。
としては、のgcdを考えればいいですね。
もっと良い方法
いま、面積がとなるような面積の三角形を構成することを考えます。 三点、からなる三角形の面積が、 となることを用います。 いま、とりあえず絶対値の中身が正になる場合を考えると、です。 このとき、をに固定し、として、残りの項で、面積を調整する ことを考えます。このとき、となります。 さて、ここまで来るととすればいいことがわかります。
こうして構築した、が長方形内に入っていることを確認してみましょう。 は明らかに大丈夫ですね。はであることから以下です。 いま、ですからが示せ、結局三角形が長方形に収まっていることがわかりました。
E - Vasya and Good Sequences
考えたこと
popcountが同じ数字は作ることができるので、数字そのものは重要ではなく、数字のpopcountのみが重要です。そこで、 の代わりに、対応する要素のpopcountの配列を考えることにします。 連続部分列がGood Sequenceになる必要十分条件は、それに含まれるの和が偶数で、 かつ、そのうち最大のをとしてが成り立つことです。
これをセグ木でも使って高速に数え上げるんだろう、と考えましたがお手上げでした。実は、問題文の条件にあったという条件が本質でした。
まず、popcountの和が偶数になる条件を考えましょう。これのみを満たす連続部分列を数えることは、累積和などを使うことによって簡単にできるので省略します。
2つ目の条件が成り立たないようなものを数え上げることにしましょう。 これが成り立たないのはの場合ですが、が高々30程度で、であることに注目すると、連続部分列の長さは長くても30程度です。なので、それぞれのをとした場合を考え、数え上げれば大丈夫です。こうしてこの問題がで解けました。
Educational Codeforces Round 51
ABCD解いて443位でした。可もなく不可もなくといった感じ。Fの解法がめっちゃ気になっているので別記事にします。
C - Vasya and Multisets
1回だけ現れる要素が偶数種類だったら同じ種類ずつ振り分けて、複数回現れる要素を片方にまとめる。
1回だけ現れる要素の種類が奇数だったら3回以上の現れるものから1個だけもらってきて、残りは↑と同様に振り分ける。
D - Bicolorings
列まで見て、個のcomponentがあって右端がBB/BW/WB/WW
int main() { i64 n, K; cin >> n >> K; if (n == 1) { cout << 2 << endl; return 0; } vvvi dp(n + 1, vvi(2 * n + 1, vi(4))); dp[1][1][0] = 1; dp[1][1][3] = 1; dp[1][2][1] = 1; dp[1][2][2] = 1; for (int i = 1; i < n; i++) { for (int k = 1; k <= 2 * n; k++) { dp[i + 1][k][0] = dp[i][k][0] + dp[i][k][1] + dp[i][k][2] + dp[i][k - 1][3]; dp[i + 1][k][0] %= MOD; dp[i + 1][k][3] = dp[i][k - 1][0] + dp[i][k][1] + dp[i][k][2] + dp[i][k][3]; dp[i + 1][k][3] %= MOD; if (k >= 2) { dp[i + 1][k][1] = dp[i][k - 1][0] + dp[i][k][1] + dp[i][k - 2][2] + dp[i][k - 1][3]; dp[i + 1][k][1] %= MOD; dp[i + 1][k][2] = dp[i][k - 1][0] + dp[i][k - 2][1] + dp[i][k][2] + dp[i][k - 1][3]; dp[i + 1][k][2] %= MOD; } } } i64 ans = (dp[n][K][0] + dp[n][K][1] + dp[n][K][2] + dp[n][K][3]) % MOD; cout << ans << endl; }