/ 最近 .rdf 追記 設定 本棚

log[AtCoder: 2021-06-17]



20210617()

最終更: 2021-06-17T19:54+0900

[AtCoder]競プロ典型 90035 - Preserve Connectivity(★7

解説はこちら>解説1解説2解説3

 LCA とダブリング

実装したことはないけどダブリング(要は繰り返し二乗法でしょ?)で効率的に親を辿る?と書いたのがつい先月のことだけどとうとうダブリングを実装する機会が訪れた

この問題がどうして選ばれた複数の頂点を決まった順番で環状に並べて隣接する頂点ペアの LCA が辺の本数になって割る2が答えになるのかは解説3を読む今は LCA にだけ注目する

LCA の取り扱い巨大企業」や筆塗りで経験があるけど線形より効率的な LCA の検索が案外面倒くさい最小共通祖先 [いかたこのたこつ]にいくつか手法がリトアップされているけど自分が知っている手段はセグメト木だけでありセグメト木は書くのが面倒くさい

そこでダブリングです。

 提出 #23482710 (TLE×26 / 810 Byte / 2207 ms / 42608 KB) ※まだコンテト期間中であり見られないはず。

間違えた(TLE)

21
22
23
24
25
26
27
28
29
30
31
32
33
34
A = [ps=P.dup]
(D.max-1).bit_length.times{ # 1世代上はすでに P としてあるので、1回分繰り返しが無駄かも。
	A << ps = N1.times.map{|a| ps[ps[a]] }
}

Ans = lambda{|a,dd|
	i = 0
	while 0<dd
		a = A[i][a] if 0<dd&1
		dd >>= 1
		i += 1
	end
	next a
}

A 配列に事前に親親の親(2世代上)4世代上8世代上とメモしておいてAns 関数(Ancestors の略ったら略)add 世代上は?という質問に対数時間で答えられるようにしたAns 関数がこのようなインタフースになっているのはLCA を経由した2頂点間の辺の数を求めたい呼び出しもと「同じ深さにある2頂点 a,b が初めて祖先を共有する深さは?という問いを立てて二分探索でその深さを確定させようとしたから次のように

35
36
37
38
39
40
41
Dst = lambda{|(a,b)|
	da,db = D[a],D[b]
	dc = (1..[da,db].min).bsearch{|dc|
		Ans[a,da-dc] != Ans[b,db-dc]
	}
	next dc ? da-dc+db-dc+2 : (da-db).abs
}

競プロにおいては対数は定数と言われるけれどかなり大きな定数ではあり必要がないところで log をくっつけると TLE に苦しめられたりする>Pairs同じ日記に書いてあるけど「射撃王」やHandshakeのように余分な log があっても TLE にならないこともある

 提出 #23482781 (AC / 873 Byte / 1082 ms / 42672 KB) ※まだコンテト期間中であり見られないはず。

A 配列は正しくはこう使う共有されている yuruhiya さんの解答 を見ても LCA#lca メソドが同じ感じだったので大嘘はついてないと思う(DstDistance の略DDepth)

35
36
37
38
39
40
41
42
43
Dst = lambda{|(a,b)|
	da,db = D[a],D[b]
	a = Ans[a,da-db] if db<da
	b = Ans[b,db-da] if da<db
	A.reverse_each{|ans|
		a,b = ans[a],ans[b] if ans[a]!=ans[b]
	} if a!=b
	next a==b ? (da-db).abs : da+db-2*D[P[a]]
}

実は解説2にはこれを説明する具体的手順が書いてある(あとで詳しく読んだ)だけどありとあらゆる落とし穴にはまりたがる自分にとって「こうすればいいんですよ「こうしてはいけませんよいうドバイスは役に立たないんですな「要は繰り返し二乗法でしというだけの理解で実装してみて「あれは良かったここはダメだったと納得するまでは身にならない仮にドバイスのおかげで最初からうまくやれたとしてもそれは今回だけのことであり将来必ず自分の性向に導かれて穴にはまる早いうちに地図を作っておくべきだそうすれば多少は


20210616()

最終更: 2021-06-21T21:06+0900

[AtCoder]競プロ典型 90068 - Paired Information(★5

すごく難しいです。答えの出し方に確証が持てないままとりあえずグラフとして解答を書いたクエリ0に応じて辺を追加するクエリ1に応じてドの値を確定しながら X から Y までグラフをたどるしかし TLE

 提出 #23506723 (TLE×10 / AC×3) ※まだコンテト期間中でありアクセスできないのでこっち>提出 #23506723.rb27

次にクエリ0では Y=X+1 だという制約を思い出してグラフを配列上に配置したしかしこれでは多少効率が良くなれど TLE は解消しない制約はド数(N)クエリ数(Q)ともに上限が 10 万であり1つのクエリに答えるのに線形時間をかけることができない

局所の変更を全体に(対数時間で)波及させるのに BIT がまず浮かぶ何を記録しよう階差の累積値かなと思ったつまり連続する3要素 A,B,C があり2度のクエリ0によって A+B=S, B+C=T であるとわかっているならC-A=T-S から1つおきの2要素の差が直接にわかるこれの累積を記録しておけばクエリ1で XY2×k はなれていても直接計算できる

 提出 #23510943 (AC / 669 Byte / 413 ms / 31888 KB) ※まだアクセスできないのでこっち>提出 #23510943.rb27

結局 BIT は使わなかったクエリへの答え方は階差の累積という点で同じだけどクエリの先読みをすれば事前に累積値が記録しておける更新がないなら値の取得に対数時間がかかる BIT よりただの配列の方が良いAmbiguous と答えるタイミングは Union-Find で管理した

 《上級者向(★7X[i] + 1 = Y[i] の制約を外した場(ワナにとても引っ掛かりやすいです

階差を使う解法は完全Y=X+1 (T=0 の場合)という制約に依存しているのでっかかりを失って困ってしまったグラフなら困らないが TLE階差を封じられてどうすればよい

 A_1A_2……A_{N-1}A_N の関係性がすべて分かっていた場合A_1 の値を +1 すると下図の通り +1 される要素と -1 される要素があります

解説が公開されたプラスかマイナスかそんな単純な関係やったんそれにしてもそこからさらに(厳しくない方の)制約をクリアさせられることがもうつらい

Y=X+1 (T=0 のと) という条件が外れた厳しい方の制約だけどX から Y へ至る異なるパスがクエリ0によって与えられたときにどうすればいいかわからないでいる定数時間で答えるためにどのような情報の持ち方をしておけばいいのか(グラフは TLE)でもプラスかマイナスかという単純な関係なのならばすべてのパスに関わるドをまとめて詰め込んでおいてつじつまが合うのだろう

連結なド間であるドを基準(たとえばゼロ)にして相対的な値を割り振っておくド間の距離(の偶奇)も記録しておく迂回路があっても分岐点・合流点のドが持つ相対値はパスによらず共通だしパスによって異なる非共通ドもド数の偶奇はたぶん同じになるまだよくわからないのは連結成分ごとに基準となるドが必要だけど連結成分同士が連結するたびに基準と相対値のふり直しをして許されるのかというところ小さい方の連結成分を選ぶようにすればいいのだろうけど

全部 Union-Find に乗せたいけどうまくいかないUnite は当然としてFind で集合の代表を書き換えるタイミングでもごにょごにょすると集合の合体があったとしても偶奇と相対値を適切に(新しい代表を基準にして)更新できると思うんだけど符号で死ぬほどバグらせる一見正しい答えに見えてもバグ×バグ=正常だったりする

 https://github.com/monnu123/atcoder_files/blob/master/kyopro90-68.cpp (monnu さん / C++ / 166 ms)

自分がやりたいのはたぶんこういうのっぱりできるんだね不可能をやろうとしてるのでないとわかったのは朗報

ところでRuby のようなスクリト言語と C++ の実行時間が接近しているときは入出力がネックになって C++ の実行時間が遅くなっている印象があるC ランタイムとの連携を切ったりstd::endl が引き起こす flush"\n" で回避したりいろいろ対処法がある模様

 https://github.com/NachiaVivias/kyopro-tenkei-90-submit-nachia/blob/main/shared-solutions/068-Paired-Information/068-01.cpp (Nachia さん / C++ / 51 ms)

っちの見た目すっきりな方はすっきりしすぎてて自分がやりたいことと同じなのかどうかもわからない

ド数が 2N あるなあ前半 N 個と後半 N 個の位置づけとは

偶数世界と奇数世界? これはド番号 X が偶数か奇数かという話ではなく二部グラフの話なのであってあるX を赤色で塗った場合と白色で塗った場合を同時並行で扱っているのではないかという……あてずっぽうです。

 提出 #23560415 (AC / 778 Byte / 334 ms / 16544 KB) ※まだアクセスできないのでこっち>提出 #23560415.rb27

った! うんざりするほどバグらせてついに完成した(UnionFind 全乗せ版)頭が整理されたところでバグまみれを捨ててイチから書き直したのが良かったと思う(それからもバグらせたんだけど)

10
11
	D[a] *= D[g]
	V[a] += D[a]*D[g]*V[g]

↑ここは無駄なことをしてる修正版はこう↓

10
11
	V[a] += D[a]*V[g]
	D[a] *= D[g]

Union-Find でグループの代表とサイズを管理するのはこれまでのおなじみだけど同時に代表からの相対値を記録するのは考えたこともやったこともなかった「重み付き UnionFindというものがあるらしいがったい何に使うものなの

ド数が 2N のすっきり解法は遠くから拝むだけにしておきます。

 https://gist.github.com/masa-aa/4be96f053457dc60625a3552288fb1e4 (masa_aa さん / PyPy3 / 304 ms)

これも重み付き UnionFindド番号の偶奇を利用すれば別途偶奇の管理をする必要がないらしいクエリへの応答部分でだけ偶奇を気にすればいいというのがよくわからないがたぶんこれを考えた先に Nachia さんのサイズ 2N のグラフがあると思う

偶奇を一体でぐちゃぐちゃ扱う(自分)→偶奇に前提をもうけて整理されたグラフを作る(masa_aa さん)→偶奇と奇偶を並行させて偶奇の別を気にしない(Nachia さん)という違いなのではないかという印象を持っている

find メソドで再帰呼び出しをしていないスタックオーバーフローのおそれがないのはいい

これだけの学びがあるのは企画の趣旨に照らして(そうでなくても)すばらしい良問だったのでは?


20210612() 競プロ典型 90065 - RGB Balls 2(★7■サンプル3で7億回ループする提出を投げた実行時間はお察し(桁が2つ多い)部分点 3+2ここからは問題をとらえ直す別のフレームが必要な感じ(=全然わかんないって言ってる)■解説2ofしかしこの考察だけだと満点が取れません次にどうすれば良いのでしょう「はいどうすればいいですか?解説3ofーワド:畳み込みを知っていますか? 名前は知っているFFT 怖い(未知のものへの恐怖)『これなら分かる応用数学教室という本を持ってるだけは持ってるんだけど16年間積んでいるっちゃんいかの人がおすすめしていたときに買った

最終更: 2021-07-12T20:54+0900

[AtCoder]東京海上日動 プログラミングコンテ2021AtCoder Regular Contest 122)A,B,C

 A 問題 Many Formulae

A 数列を後ろから見ていく方針は早くに決まったのだけど初項を [1,0] ではなく [1,1] にしていたミスでいつまでも答えが合わなかったあと余りを取り忘れて 1 TLE

 提出 #23369338 (AC / 147 Byte / 106 ms / 24936 KB)

もう 54 分経っている

 B 問題 Insurance

A 数列をソトすれば賢く答えが求められそうだけど何も考えずに探索しても良さそうこの前の ABC204-E Rush Hour 2 と違って最初から実数解が求められているあたり罠もなく素直なのでは?

デバッグ出力を消し忘れて 1 WAbsearch メソドで極小値を求めようとして 1 WA三分探索(名前だけは知っていたが初めて書いた三分探索を救いたい - Qiita)100 回試行しようとして 1 TLEっくり半分の 50 回にして AC本当は探索範囲の幅が許容誤差以下になったかどうかを終了条件にすべきだったそれもダメ?

AC までおおよそ 30 分だから B 問題は A 問題より簡単

 C 問題 Calculator

盲滅法な探索では時間が足りない考えても時間の無駄なので興味本位で解説を読んだ>C - Calculator 解説 by maroonrk_admin

ボナッチ数? 競プロ典型 90でも見かけたこの数列がどうして頻繁にあちこちに登場するのかわからない限りなくありふれたジェネレータなのか(知らなかったけど A 問題にもボナッチ数が現れていたらしい)あと計算途中で1を足すという行為が新しい系列のボナッチ数列を開始することそれらずれたボナッチ数列を串刺しにした和が数列として現れるということもあまりピンとこないそうなの? そんなこと漸化式を見てわかる? 足し算とはそういうものだと言われたら言葉がない私は足し算がわかりません

とりあえず実験をしたx,y=1,1 を初項として操作3と4を繰り返すとどのように値が増加する大体 80 数回で N の上限を超える今度は x,y=0,0 でスタトして 80 数回の1か所でだけ +1 をすると80 数回の操作34の結果がどういう値になるのかを調べたこれは並べるとボナッチ数列になった

つまり次の提出における A 数列というのはボナッチ数列を貼り付けたものではなく+1 するタイミングによって操作3と4を繰り返したのちのちにどれだけの影響を及ぼすかというのを予め調べた実験結果なのである

 提出 #23379308 (半分くらい WA)

貪欲をすればよいと解説に書いてあったので貪欲をしたボナッチ数列の増加のしかたを見れば組み合わせでどんな数字でも作れそうな雰囲気はある(基数の冪乗を大きい方から取っていくのと同じように)

この時,ii+1 両方で操作することはありませ なぜなら,は読み飛ばした解説の細かい部分にこだわってもわかりません

WA の原因は問題を読み誤っていたことxy のどちらかが N になればいいと思っていたがそうではない

 提出 #23379741 (AC)

xy を取り替えればいいのだから仮の操作列を作ってから必要に応じて操作1と2操作3と4を交換するようにした最初からきれいな解答を作ろうという努力はあきらめている

ARC-C が自分のいるところからどれだけ高くにあるかは垣間見られたのでは? 時間内に B 問題まで解けただけで上出来なのにレイングは下がるんですよ緑に対してあまりにひどい仕打ちAB がどちらも茶 diff だといってもARC に参加する層にとっての茶 diff だという意味でABC の茶 diff 問題とはくらべられないと思うんだへなちょこながら ARC に参加する意気にレイングで報いてほしい(嘘です)

それもダメ? [[実数の二分探索・三分探索はループ回数を決め打ちしようね - えびちゃんの日記|https://rsk0315.hatenablog.com/entry/2020/04/29/155009]]探索範囲が過大(過小)な値に寄っていったときに浮動小数点数の密度が足りなくなって無限ループする? 無限ループしていないことをもって OK としていい?


20210607()

最終更: 2021-09-14T17:27+0900

[AtCoder]競プロ典型 90059 - Many Graph Queries(★7

AtCoder タグを付けてるけど非公式コンテトです。

この問題はとっかかりがなくて最初から解説を読んだ>解説1解説2解説3

 ーワDAG を使いこなそう

この問題で扱われるグラフは DAG (有向非巡回グラ) といわれるものでサイクル() を含まない有向グラフです。

っ? どこに閉路を含まないって書いてあった? 問題文を3回読み直して気がついた

i は頂点 Xi から Yi に向かいます。

制約 1Xi<YiN

制約が持つ情報量が多すぎて見逃していた難しいそして閉路が含まれてるなら含まれてるで解説3に書かれている強連結成分(SCC)分解を手段として持っておくべきであると

 提出1 (TLE×6 / 3325 ms / 461844 KB) 得点 2/7

まだコンテト期間中だけどソースコド共有の動きがあるくらいなので隠す必要はないかなと>解説が公開されたらースコドを共有していきましょう

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
N,M,Q = gets.split.map(&:to_i)
B = 60 # Bignum に切り替わらないビット数を。
Z = (N-1)/B+2
V = Array.new(N+1){|a| [0]*(Z-a/B) }
E = Array.new(N+1){[]}; M.times{
	x,y = gets.split
	E[x.to_i]<<y.to_i
}

N.downto(1){|a|
	va = V[a]
	va[-1] |= 1<<a%B
	E[a].each{|b|
		V[b].each_with_index{|v,i|
			va[i] |= v
		} if va[a/B-b/B-1][b%B]<1
	}
}

puts$<.map{|ln|
	a,b = ln.split.map(&:to_i)
	next %w[No Yes][V[a][a/B-b/B-1][b%B]]
}

2枚目の解説画像で満点解答のため「ビト演算で 64 倍高速化と書かれていることからRuby で満点解答が望めないのは明らRuby を使うことによる定数倍のハンデが最初から 100 より大きいので……手元では V = の初期化部分だけで23秒かかってるもんね部分点2で満足している

AtCoder はスクリト言語に優しいので定数倍高速化がキーになるのは非公式コンテトならではかな解説が大いに参考になる

昨日の ABC204-D - Cooking の解法はどこか(解説1)で見たような形だなあと思いながら書いていた(この日記を書いてるのは月曜だけど典型90-059の出題は土曜日だったので日曜の ABC の前に取りかかっていた)

 解説と違う点

ーワドからこうだろうと最初に思いついた方法で実装したら解説とちっと違うって良くない

最初にすべての頂点について到達可能な点を(ーにビト演算を使って)調べてからクエリに定数時間で答えるというのがさっき貼り付けたスクリトだけど解説ではクエリを 64 並列で調べるようにしていたクエリが起点にある

調べたら重いケースでは頂点数(N=100000)クエリ数(Q=100000)に対して実際に到達可能性が参照される頂点の種類が 84000っとになるものがほとんどだった(例外が 99999)16000 の頂点に関しては到達可能地点を調べたのが無駄だったまあ無駄を省こうとするとスタックオーバーフローを避けてごてごてと記述と条件判断が増えるわりに2割も違わない(それも入力依存)という見かたはできる

必ずしも良くないばかりでもなくて頂点数 N に比して辺数 M が大きい場合想定解法の方が遅くなる

 提出2 (MLE×1 / 1154 ms / 1244032 KB)

基本は提出1と同じ3060トでテーに区切っていた最大で 10 万ビトになりうるビトフラグを1つの Bignum にまとめた

多倍長が許されるのは数百桁までかなと思ってたんだけど(最大で)10万桁でもすっごく速かったしかしメモリ制限 1024 MB を超えて 1.24 GB 使ったところで MLE

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
N,M,Q = gets.split.map(&:to_i)
E = Array.new(N){[]}; M.times{
	x,y = gets.split
	E[N-x.to_i]<<N-y.to_i
}
V = [nil]*N
N.times{|a|
	va,bs,b = 1<<a,E[a]
	va |= V[b] if va[b]<1 while b = bs.pop
	V[a] = va
}

$<.each{|ln|
	a,b = ln.split.map(&:to_i)
	puts %w[No Yes][V[N-a][N-b]]
}

頂点番号とビト位置の対応を逆向きにしたのが工夫(提出1でも Z-a/B という形でやっていた)たとえば N-1 番目の頂点が N 番目に移動するというのを 0b11 で表現すれば Bignum はいらないこれを素直に 1<<N-1|1<<N で表していたらほぼ全ての頂点で N 桁の Bignum が必要になってもおかしくない(頂点1以下全ての頂点が頂点 N に遷移する場合)しかしあえなく MLE工夫なしだと 1.29 GBったから 50 MB だけ減りましたよ

たぶん Bignum のネックは桁数に比例する部分ではなく100 桁であれ1万桁であれ必ず必要になる基礎的な部分でのコトなのだろうその面では 64ト版が不利だBignum か非 Bignum かで見れば上の段落で改善するのは 10 万頂点のうちの 62 頂点程度に過ぎない桁数部分での寄与は小さい

 提出3 (AC / 2009 ms / 500612 KB) 得点 7/7

これは提出12と違って想定解法と同じ方針ただしクエリ 64 並列ではなく 10 万並列でやっている

DP がしたいのに Bignum を頂点の数だけ保持すると MLE になるのをどうすれば良いTLE を避けたいなら Bignum を使うしかないがどうすれば良い頂点番号の小さい順にどんどん処理を進めて使用済みの情報は辺もクエリも遷移してきたという DP 要素もどんどん捨てていった一時的には C 配列に処理待ちの Bignum が蓄えられるけどどこかから遷移して来るまではそれも 0 (Fixnum)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
N,M,Q = gets.split.map(&:to_i)
E = M.times.map{ gets.split.map(&:to_i) }.sort_by(&:first)
A = Array.new(N){[]}
B = Array.new(N){[]}
Q.times{|q|
	a,b = gets.split
	A[-a.to_i]<<q
	B[-b.to_i]<<q
}

r = 0
C = [0]*N
N.times{
	as,bs,c,q = A.pop,B.pop,C.pop
	c |= 1<<q while q = as.pop
	r |= c[q]<<q while q = bs.pop
	C[N-E.shift[1]] |= c while e = E[0] and e[0]+A.size==N
}

Q.times{|q|
	puts %w[No Yes][r[q]]
}

Ruby で満点がありだったのだなあ道具のせいにしてあきらめなくて良かった

 提出4 (AC / 1376 ms / 383080 KB)

メモリもタイムもさらに良くなった提出3がベースだけどクエリごとに1ビトを使うのではなくスタト地点 A が共通するクエリが同じビトを共有する

こちらを参考にして

昨日はうしさんのライブラリを参考にして解いたため(A_i,B_i)というクエリを処理するときにはdp[A_i]|=1<<iとしていたそうではなくdp[u]|=1<<uとしすべてのペアについて問題を解くことにすれば頂点番号uに対しては1..uに対応するbitしか保存しなくてよいため必要なメモリが\frac{N^2}2くらいになるらしい

2021/05/31-2021/06/06 - kotatsugameの日記

頂点番号が小さい順に処理をするという提出3の流れと頂点番号が小さいものから0に近いビトを割り当てるというのがマッチしてトの同時使用量が節約できる

整理したからそのまま同じではないけどだいたいこんな雰囲気>typical90_59_nosub5.rb27

@2021-09-14 結局最後のも提出した>提出 #25841973 (1149 ms)


20210604()

最終更: 2021-06-05T05:03+0900

[AtCoder] 競プロ典型 90057 - Flip Flap(★6

解説画像はこちら>https://github.com/E869120/kyopro_educational_90/blob/main/editorial/057.jpg

この問題はとても良かった確実に力になった問題を解く選択肢が増えたーワ「行列の掃き出し法を知ろうって

最初は独力でパネルを順番に見ていく DFS を書いたそれしか知らないだけどどうしても入力に左右されるしあるパネルに影響する複数のスイッチの組み合わせを総当たりするので遅くなる

入力を前処理して扱いやすくするとは考えてもみなかった

でまあ今後はこういうことができると知ったはいいけどこれが適用できる対象の性質を見抜けるかどうかというところに課題が移ったとみていいのではないかな簡単ではないね

具体的な実装にも迷いがあるスイッチ数(N)パネル数(M)の上限が 300 なのに対して 550 ms の時間をかけているメインループだけど

  1. スイッチ群から最も左のビトが立っているスイッチを選んで取り出す
  2. 残されたスイッチ群を処理して最も左のビトが取り出したスイッチのものより右にくるようにする
  3. 取り出したスイッチを押したり押さなかったりする(最も左のビトが立っているスイッチはこれ1つだけなので確実にどちらかを選べ)
  4. 繰り返し

ステップ1の処理が Nステップ2の処理が N×Mっとシッと片付くうまい書き方があっても良さそうでは?


hai!@magrofly「典型057 Ruby https://t.co/Dri1yBSvuK / Twitter

「基底を求めるのはよくわからないけど「解く過程では順番に適用していくだけでいいようだっきの手順に当てはめると基底を求めるのがステップ2に相当して解く部分がステップ3ステップ1の検索が不要であると

 549 ms

最初の提出無駄にソ検索している(9行目)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
N,M = gets.split.map(&:to_i)
A = N.times.map{
	gets
	next gets.split.map{_1.to_i-1}
}
S = gets.split.map(&:to_i)

n,T = 0,[0]*M
while as = A.sort_by!(&:first).shift
	i = as[0]
	A.map!{|bs|
		i < bs[0] ? bs : ((as|bs)-(as&bs)).sort
	}.reject!{|bs|
		bs.empty? && n+=1
	}
	as.each{|i| T[i] = 1-T[i] } if S[i]!=T[i]
end

p(S==T ? 2.pow(n,998244353) : 0)
 524 ms

ツイトを参考に改良したがあまり良くはならないN,M<=300 だから? 対称差を求める部分(15行目)が富豪的過ぎるから? 前処理の部分でまだちっと変なやり方をしてる?

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
N,M = gets.split.map(&:to_i)
A = N.times.map{
	gets
	next gets.split.map{_1.to_i-1}
}
S = gets.split.map(&:to_i)

N.times{|i|
	next if A[i].empty?
	(i+1...N).each{|j|
		next if A[j].empty?
		if A[j][0] < A[i][0]
			A[i],A[j] = A[j],A[i]
		elsif A[j][0] == A[i][0]
			A[j] = ((A[i]|A[j])-(A[i]&A[j])).sort
		end
	}
}
A.reject!(&:empty?)
O = N-A.size

T,as = [0]*M,nil
as.each{|i| T[i] = 1-T[i] } if S[as[0]]!=T[as[0]] while as = A.shift

p(S==T ? 2.pow(O,998244353) : 0)

最終更: 2021-06-24T16:08+0900

[AtCoder] 競プロ典型 90055 - Select 5(★2

★2つだからってこういう素直なスクリトを投げた

N,P,Q,*A = $<.read.split.map(&:to_i)
p A.combination(5).count{|a5| Q == a5.inject{_1*_2%P} }

結果は TLE×21/AC×4なんだよ全然ダメじゃんかよ解説画像はこちら>https://github.com/E869120/kyopro_educational_90/blob/main/editorial/055.jpgRuby では解説が役に立たない★6~7相当のより高速な解法があるというからそちらを解説してもらわねば


ABC192-F Potion (20210224p01) を思い出しながら悪あがきをした

N,P,Q,*A = $<.read.split.map(&:to_i)
q = Hash.new 0
S = A.map{|a| q[a%P]+=1; q.dup }
(N-1).downto(N-4){|k|
	S.pop # S.size==k
	q = Hash.new 0
	S.map!.with_index(N-k){|q0,i|
		a = A[i]
		q0.each{ q[_1*a%P]+=_2 }
		next q.dup
	}
}
p q[Q]

これで TLE×6/AC×190Q<P10^9 という制約がえぐすぎるんよな大きすぎるし素数じゃないしったいどういう手が打てるの


N100 以下というところで何かできないかと考えた3..100 のある i より左から A×A のペアを作って記録しまた 1..98 のある i より右から A×A のペアを作って記録し3..9896 通りのある A_i に関して i の左右から条件を満たすペアを引っぱって来られないかと考えたP で割った余りは 10^9 通りになりうるけどA×A%P が取り得る値は 10000 通りより少ないから左側のペア×A_i を総当たりしても高々 1000000 通りでありこれは許されるあとは右側のペアが定数時間で選べれば良いここでモジュラ逆数が使えるような使いたいような気がするんだけど法が素数でないかam と互いに素でなければならずさもなくば逆元 x は存在しないWikipedia に書かれていて困っているA×A×A%P の値が決まっているときA×A%P の値が何であれば A×A×A×A×A%P==Q となるたぶん1つに定まらないのではないかと思うここで詰まった


Ruby を使う良さってこういうところだよねぬるい解法が許されないところ(許されるとそれ以上考えないので)Ruby ならではのお楽しみポイこの前の日記(20200907p01)で散々 TLE に苦しめられた問題もC++ なら変数 rmap変数 nminmultiset にすることである範囲のキーを二分探索で検索することも小さい方からキーを取り出すこともSTL 任せで妥当な時間で行える適当に速くて短い提出を選んだけどこんな感じ>「提出 #16578878トリックは必要ない そこは問題の本質ではないからかかずらいたくないという考えもあるだろうけど自分が一番楽しめるのはここ


Ruby365 ms の解答が共有されているなあ見たい見たくない見たくない


典型 90-005 の解説から>mod の値が 998244353 のような特殊な値ではないので一工夫必要ですがそれでもたとえば 3 種類くらいmod を用意して中国剰余定理で復元する方法などで上手くいきます中国剰余定理は Wikipedia やブログを読む機会が何度かあったけどまだ早いと理解をあきらめている状態


線形篩を用いた合成数を法とする逆元の列挙

最終更: 2021-06-08T14:11+0900

[AtCoder]AtCoder Beginner Contest 170F 問題 Pond Skater

競プロ典型 90 問の 043 - Maze Challenge with Lack of Sleep(★4が普通に解けたのでPond Skater (diff1968)も解けるような気がした(既視感>20200826p01根拠は必要ない)

 提出 #23175112 (AC / 777 Byte / 492 ms / 46772 KB)

All AC になるまで死ぬほどバグらせたこれが時間内に解ける未来が見えない訪問済みフラグが二値ではないープの継続条件とキーへの追加条件が同じではないープのあいだ無条件にキーに追加していると rand_20_01.txtrand_20_03.txt の2つのケースでキーが複製要素で膨れ上がってしまうそれ以外のケースでは AC なのに

無駄に難しくしてる? そうであってほしい

 Ruby でのすべての提出 (AC のみ / 実行時間昇)

比較すると短いし速いし省メモリだと思うトケースハック(※テトケースは公開されている)らしき提出を除けば

訪問済みフラグを水平方向と垂直方向に分けたのが間違いだったろう途中からフラグ(二値)ではなくなったし紆余曲折を経て扱いが同列だし単にコトとして再定義して書き直せるのでは?

 提出 #23175271 (AC / 590 Byte / 374 ms / 39016 KB)

VhVvったものを C に統合したさらに短くさらに速くさらに省メモリになった最初にコトでなく2つのフラグを使って書き始めたのが間違いの始まりだったのだなあ(Maze Challenge with Lack of Sleep(★4の解き方がそうだったからなんだけど)


20210522() 怖くて見てなかったけどこの前の ARC (20210510)のパフォが 300 台だった! えつまり灰パフォ? これが初めてではないしむしろ大体そうなんだけどARC の傷を癒すには ABC1回では足りないぜなんなら ABCD 問題にだってたまに……ときどきやられる■明日の ARC (20時から!)のお誘いメールが来ない……A 問題から 400 点なのはたいへん厳しいARC400 点は五分五分だ

最終更: 2021-06-08T14:41+0900

[AtCoder] エイシングプログラミングコンテ2021AtCoder Beginner Contest 202E 問題 Count Descendants

解けてしまった問題とまるで歯が立たなかった問題は日記にならなくて困るという点で共通するE 問題で RubyAC 提出をざっと見たら他の人の解法が異なっていたのでそれを

ちなみに F 問題の提出(Ruby)はなかったゼロだったまずね凸包がよくないそれは自分の人生とは一切関わることのない専門用語(だということにしてこれまで無視してきたもの)なので

話は E 問題こちらはやること自体は明白で読めばわかる木が与えられて深さを調べるのも親を辿るのも基本操作だこの問題の問題は制約にあってド数もクエリの数も上限が20万だというところ定数時間でクエリに答えたい

 クエリ(U,D)に答える方法1

  1. ある深さ D にあるドのうち祖先に U を持つものを選ぼう
  2. 実装したことはないけどダブリング(要は繰り返し二乗法でしょ?)で効率的に親を辿る?
  3. (ほぼ)ドがフラトに並んでたらダブリング関係なくクエリ(N)×ある深さのド数(N)で死ぬ

 クエリ(U,D)に答える方法2

  1. あるU の子孫ドを深さごとに数えておこう
  2. 深さを調べる深さ優先探索のついでにそれはできるけどドが記録した配列をマージするのが大変ほぼドの数だけマージ操作が必要。ージテク
  3. 実装しなかったのでこれがアリなのかわからないドのうち底が深くないものから深い方へ順番に足し合わせていくとどうなる

    一直線の木で Σk 個の要素を処理するような実装でなければアリなの

 提出 #22827097 (TLE×27 / AC×3)

これはマージしないでいいように1つの深さ記録配列を共有する実装ただしドごとに Array#dup した配列をスナップシトとして持っている配列の要素数の合計がギガ単位になるけどどうかなと期待して出したけど全然ダメだったAtCoder の入力マジえげつない上限は飾りじゃないのよ

 提出 #22832306 (AC / 708 ms / 285,364 KB)

基本形はさっきのと同じクエリを先読みして必要なものだけ記録するようにしたTLE から 22 分経過していてコンテト終了まで残り 10あぶなかった

 クエリ(U,D)に答える方法3

これは他の人の AC 提出を読んで知った方法二分探索を使うらしいオイラーツアー?

  1. 深さ優先探索で深さを調べると同時にあるドに降りてきたタイミングと上っていくタイミングを記録しておく
  2. クエリがきたらU の両タイミングの間に処理をした(=子孫)のうちある深さ D にあるドの数を答える
  3. タイミング配列をなめて深さを確かめる線形時間の処理は許されないどうする?
  4. (3つの提出を読んだのに思い出せな) どうする?
  5. タイミング配列を深さごとに分けておくのかな
  6. クエリが指定する深さのタイミング配列を取り出してそこからクエリが指定するドの子孫にあたる範囲を二分探索で切り出すと答えがわかる……かな? 実装して AC をもらってないので不確

確かなことはこのあたりの提出を読めばわかる(提出の早い)

メモリ消費にけっこう差があって自分の提出は最も多い部類正直どこが原因なのかわからない


20210427()

最終更: 2021-04-30T21:11+0900

[AtCoder] AtCoder Beginner Contest 199Sponsored by PanasonicD 問題 RGB Coloring 2

10分間3問早解き回だったわけなので4問目(D 問題)は時間中に解けなかった90 分使っても解けなかった辺がないケースで TLE が避けられなかった

 提出 #22101036 (AC / 343 ms)

AC のきっかけはこのツイ

chokudai(高橋 直大)ナス@chokudai

D問題非連結ですって言うだけでDiff400くらい落ちそう

Twitter

chokudai(高橋 直大)ナス@chokudai

非連結ですって言っても落ちない(サンプルにあるし

連結で出題しないと400落ちなさそう

Twitter

連結で出題しないと落ちないがよくわからないけどともかく非連結なら問題を分割できるじゃんと気がつきましたツイトを読んで初めて気がつきました

サンプル4つのうち2つが辺がゼロのケースだったんだけど極端すぎてそれが全体としては非連結なグラフであること個々の頂点としては最も簡単な連結成分を構成しているということがわかりませんでしたそんなことってある?

 AC 前の提出 #22100473 (TLE×2 / 1つは after_contest)

トケースが公開されていないので提出前のテトには一直線の木を使っていた連結成分の数を増やしても辺の数を増やしても探索を助ける制約が増えるだけだと思ったので

しかしまだ TLEどういう木が嫌か考えるとこの提出は次のような入力に弱い

だけど次のように番号の付け方を変えただけで問題が問題でなくなる

たぶん頂点を次数でソトしてから DFS をすれば緩和されたと思う(AC 提出では違う方法(先読み)をとった)

トするにせよ先読みするにせよ2番目の問題に対処するには非連結なグラフを連結成分に分割する必要があったがそれをしないまま DFS の処理量を削減する方法を考えていたというのが失敗に終わったコンテト中の時間の使い方だった

 提出 #22109995 (AC / 238 ms)

DFS の処理順を次数の降順にしたら悪くなりにくいのか343 ms からタイムが大いに改善したこのムラが DFS の沼であり抜け出せない楽しみなんだよなあ>20201101p0220201107p01.05


20210412()

最終更: 2021-06-08T15:01+0900

[AtCoder] AtCoder Beginner Contest 198D 問題 Send More Money

昨日あった ABCD 問題は覆面算たまたま何か月か前FDCAJH × IBCFEH = FBAECIIJEGIHというのを解く機会があったのだけど時間制限がないせいで雑に総当たりをして済ませてしまっていた

 提出 #21665467 (TLE×6 / AC×34)

本番中は TLE で終わってしまったE 問題を 15 分で片付けて戻ってきたけどついに通せなかった

 提出 #21721083 (AC / 3703 ms)

制限時間が大盤振る舞いの5秒なんだよね

桁を1つ2つ減らすだけで時間がだいぶ違うだろうという予測はできたけど減らし方がわからなかったなんといっても目の前に文字で書かれた式があるわけではなく色々なケースが入力されるわけなので

TODO: Array#all? の中のテトは l<=r より l==r||l+1==r の方が厳しくて良い

TODO: 和の先頭の桁が1だとすぐにわかる場合がある

TODO: 列挙してから弾くより列挙しない方がいい(確定桁が1つあったとして未確定桁(=文字種-1)の順列の数だけ弾くのは手間だか)

TODO:ープの中の処理がシンプルになるように入念に事前準備をした方がいい

 Ruby によるすべての提出

現在 Ruby での AC 提出は 20実行時間が 109 ms から 4845 ms までと幅広い中央値は3秒台です。

たとえば(4桁 ms では最も速い)1.6 秒のこの提出>#21688714

先頭桁が0のケースを弾くと同時に末尾の桁が一致するかどうかだけ特別にチックしている一致しないケースでは文字式の全体を数値化する無駄がスキップできるこのひと手間が効果的なのだと思う

それと全くの想定外だったのだけど文字が 11 種類以上使われている式が入力されるケースがあったのだろうか(上の 1.6 秒の提出がチックして UNSOLVABLE を出力している)AtCoder の問題は入力や条件がきれいに整理されていて枝葉の手間が省けるように作られているだろうという甘えがあるのは否めない

3つある3桁 ms の提出が何をやっているのかはっぱりわかりません

 提出 #21741214 (AC / 921 ms)

4つの TODO を意識して書いたけど妥協した部分もある

  • 妥協1:確定した1を他の非ゼロと混ぜて列挙した
  • 妥協2:列挙を正と非負の2つに分けたがどちらにも permutation メソドを使いたかったがために配列の引き算やら配列の2段参照がループの中にある

とはいえこれを深さ優先探索で妥協なく書き換えただけで2桁 ms になる? そうRuby で現在最速の提出は 71 ms になっている

根本的なところで列挙してから弾くか可能性のある組み合わせだけを列挙するかという違いがあるのかなっち方面で書こうとしたときはある桁を見たときに未確定文字が0なのか1個あるのか2個か3個か未確定文字があるのはどの項か繰り上がりはあるのかということを考えるのが面倒くさくなって(=脳のキャパシをオーバーして)書けなかったんだよね

 提出 #21743144 (WA×1 / 90 ms)

書けなかったのをがんばって書いた時間は申し分ないけども1つの WAたぶん答えがないケースだと思うんだけど……

 提出 #21743445 (AC / 66 ms)

WA の原因は非ゼロチックが1つ抜けていたことそれと想定外だと書い「文字が 11 種類以上使われているケースはサンプル4がそうだったコピペするだけで全然読んでいない

 FDCAJH × IBCFEH = FBAECIIJEGIH

雑に総当たりしていたのを反省して(TLE は嫌だ!)数か月ぶりに書き直した提出 #21743445 をベースにして掛け算に対応させたprd の計算が難しかったのですよありえたかもしれないもうひとつの筆算のかたちっごく縦長になるけども

A = 'FDCAJH'.bytes.to_a # to_a is for Ruby 1.8/1.9
B = 'IBCFEH'.bytes.to_a
P = 'FBAECIIJEGIH'.bytes.to_a

C2D = [nil]*91
D2C = [nil]*10
NZ = [-1]*91; NZ[A[0]] = NZ[B[0]] = NZ[P[0]] = 0
F = lambda{|i,carry,aa,bb,zz|
	next carry<1 if i<-P.size

	a = (c = A[i]) ? C2D[c] : 0
	b = (c = B[i]) ? C2D[c] : 0 if a
	next D2C.each_with_index.any?{|e,d|
		next if e || d==NZ[c]
		C2D[c],D2C[d] = d,c
		next F[i,carry,aa,bb,zz] || C2D[c] = D2C[d] = nil
	} unless b

	prd = a*bb+b*aa+a*b*zz+carry

	if p = C2D[c=P[i]]
		next p==prd%10 && F[i-1,prd/10,a*zz+aa,b*zz+bb,zz*10]
	else
		p = prd%10
		next if D2C[p] || p==NZ[c]
		C2D[c],D2C[p] = p,c
		next F[i-1,prd/10,a*zz+aa,b*zz+bb,zz*10] || C2D[c] = D2C[p] = nil
	end
}
raise unless F[-1,0,0,0,1]

puts [A,B,P].map{|a|'%*d'%[P.size,a.inject(0){|b,c| b*10+C2D[c] }]}

20210406()

最終更: 2021-06-02T21:11+0900

[AtCoder] AtCoder Beginner Contest 004D 問題 マーブル

緑がほぼ埋まってきて残っているのは解けなかった問題ばかりそこで水色下位に手を出すも下位とはいえ水色はぱっぱっと解ける雰囲気ではないあれもこれも行列の問題で問題のその操作で何ができるのかさっぱりわからない

だから青色難しかったん1年くらい前に ABC004 を埋めようとしたときは力が及ばず C 問題までしか提出に至っていなかった

 提出 #21534453 (WA×2 / AC×83)

今回も一発 AC とはいかなかった原因はすぐに推測できて緑色が原点から離れない想定が誤っていたのだと思った

たとえば赤か青の片方が極端に多いとき外側に広がっていくよりも中心にある緑色の全体を移動させてでも中心に向けて移動する方が低コトになる分岐点がある

しかしそれを想定するとコドにするのがさらに難しくなりそうで困った

ちなみにこの提出の方針は……赤と青をそれぞれ -100100 を中心にして原点の左右で平らに並べる原点は超えない数が多ければ外側により大きく広がるそのあとで緑色を原点を中心として配置していく左右のコトを比較して赤と青を押しのけながら

提出に至らなかった1年前の方針はRGB の数から重心を求めて云々という感じっとすると緑の配置拠点を原点に限らず適切に移動することでWAった方針のまま AC に持って行けた可能性が?

 提出 #21541500 (AC / 1246 Byte / 65 ms)

J - 長い長い文字列(提出 #19035422) とかK - 転倒数(提出 #18029328)とか脳みそに余裕がなくなるとクラスや日本語変数がソースに現れる傾向があるみたい今回は両方出てきた(クラスのメソドの並びが不揃いなのが気になる左を先に書くで統一しておきたか)

イメージとしてはビー玉をざらざらと流し込んでから抵抗の強弱を感じ取りつつ右に左に均す感じ最大で900個程度の広がりしか考えなくていいからなんとかなっている

Ruby の他の提出を見るとゴルフをしていなくても 300ト台の短い提出がいくつもあるし内容も候補を並べて最小値を選ぶ二分探索で解(極小値)を探すなど特に大層な道具は必要としていないそれは頭の中で十分に理解して整理できているから書けるんだよなあ

できないからソースコド上でメソドと複数のイスタスに分割して整理しています。結果としてひと味違った解法になったと思う

 @2021-04-07

たぶん抗力の計算が間違ってるんだよね

押した力を上限として0以上それ以下の力しか発生しないはずだけどなんだか負の抗力によって隣の障害物に引っぱられていきそうになってるそれだと引っぱってる方はともかく引っぱられる方は必ずしも安定した低いエネルギー状態にあるとはいえなくなる

これが問題にならない理由もわかるけどそれはクラスの外部スタスの利用方法にあるのであってクラスのメソドの定義としては間違っている


20210401()

最終更: 2021-06-08T15:27+0900

[AtCoder] AtCoder Beginner Contest 155D 問題 Pairs

ABC の4問目で 400 点問題しかし青diffではある

 未提出 ABC155_d.rb27 (TLE)

時間制限を 10 秒にしてくれたらたぶん通るしかし実際の制限は2秒であり3秒ですらない慈悲はないの

Ruby の提出一覧を見ると AC していても軒並み1秒越えであり処理量がしんどい問題なのは間違いないのだけどその中にあって1秒を切っている提出もあるということは己の考えが足りないのであるぐぬぬ

 方針

入力を正負ゼロに分けて正負ゼロの積がそれぞれいくつ作られるかをまず求めた

負の積が K 個かそれより多いならば正の数と負の数のペアを考えるゼロは特に考えることがないK 番目が正の積の中に含まれているなら負の数同士のペアと正の数同士のペアを考える

これで考えるべき組み合わせが多少は減ったつもりになるが入力次第では何の足しにもならない本質的に計算量を削減する方法がわからなかった

それでどうしている

K 番目の数を -10^{18} から 10^{18} の範囲で二分探索している

ペアをある数とそれに掛け合わせるソト数列として持っているK 番目の数の候補となる数が与えられたときその数以下の積がいくつ作られるかはこれまたソト数列を二分探索することでわかる

ペアの数が馬鹿にならないN (2×10^5) のオーダーで存在するだか「ある数」と「ソト数列に注目してペアをソトされた状態で持っているそうすると K 番目の数の候補となる数が与えられたときかすりもしないペアを予め除外して考えることができるかすらないとは2通りあってすべての積がある数以下となるかすべての積がある数より大きくなる全か無ここで累積和と三度目になる二分探索を使っている

とまあこんな感じ(3つだが三重ではない)二重の二分探索のあいだに範囲を絞っているとはいえちまちまと順番に数え上げる線形時間の処理が挟まっているのがいただけない一番重たいケースで 10 秒はがんばった方だと思うよ知的方面でのがんばりではないけども


ト列とソト列の組み合わせでペアを作っているのにそのときに一方のソト列をばらばらにしてしまっているのが悪いのか? (短い方を選んでバラすようにはしてい)


 AtCoder Beginner Contest 174E 問題 Logs

この回 C 問題が解けなくて大爆死した回の ABCその後 C 問題を解いてF 問題も解いたけどF 問題が解けたら DE も解けたつもりでいいんじゃないかな?と書いたようにF の後でも DE が解けていなかった不思議なものでD 問題は緑埋めをしていた先月に普通に解いていた(提出 #21267825)緑がほぼ埋まってきて次なるターゲトは水色下位に移ってきているE 問題 Logs である解けない緑より解ける水色なのである

 提出 #21466620 (AC / 226 Byte / 350 ms)

解けました解けなかったときは何を考えて行き詰まっていた

  1. 最優先で切断する丸太はその時点で最も長い丸太である
  2. 2等分しますか? 3等分しますか? そもそも等分しますか?
  3. たとえば最終的な解が2等分した長さより短く3等分したよりも長くなるなら2等分したあとでその両方をさらにもう1回ずつ合計で3回分割する手間をかけるのは間違いである
  4. 解がわかっているなら最初から2回の手間で3つに分けるのが最適だと判る
  5. しかし解がわからない

今日の日記のタトルD 問題 Pairsす。関連は?

これまで二分探索といえばソト済み配列から特定の閾値をまたぐ値を選び出すのに使用してきたのだけど実はそれだけではなかった何もない空中から特定の値()を見つけ出すのにも利用できるのだった順序さえ与えられるなら解が -10^{18} から 10^{18} の範囲に存在すると判っているならったひとつの意味のある値()を二分探索してもいいのである

という気付きが Pairs を解く過程で(まだ解けてないけど)得られていたので今度はごく素直に解を決め打ってから最適な切断をすると切断回数の合計が何回になるかという逆算的な解法を発想することができたそういうことができるとわかっていた

二分探索を使った解法でかつて最も衝撃を受けたのは Vacant Seat というインタラクブ問題に対する提出 #2057817#2064531ったbsearch メソドから呼び出されるブロックの中でクエリを行っているいやね自分も提出 #7970588 の中で二分探索を使って答えを出してるんだけどそのことと対象となる具体的なソト列がないまま空中で二分探索を行う順序はクエリで動的に決定するということの間にどれだけの隔たりがあること

脳みそが不自由だと存在しない制約で思考が枷をはめられてしまうのだなあ最も基本的なツールといえる二分探索もまだまだ使いこなせていないのだった


ところで 350 msRuby で2番目に速い提出なのだけどどんぐりの背比べである2番目とそれ以降から頭ひとつ抜けて速いのがこの 提出 #15632506 (sushibon さん / 219 ms)二分探索は行っていない(トはしている)

二分探索というのは人間が考えることを放棄して機械に試行錯誤させる解法なのだけど人間が頭を使えば無駄なく速く答えを求めることができるのですねまあ何をどう考えればいいのかわかりませんけども

 AtCoder Beginner Contest 023D 問題 射撃王

これも空中二分探索解を決め打ってから考えるもはやおなじみである

 提出 #21974701 (AC / 245 Byte / 953 ms / 21392 KB)

Ruby では唯一3桁 ms に入った(他は4桁)log1つ分の差だと思うNlog^2Nlog単にソトする方のやり方を思いつかなかっただけなんだけど

 AtCoder Beginner Contest 149E 問題 Handshake

同じ青diffでもこちらのほうが Pairs よりわずかに難しいことになっている

 提出 #22314080 (AC / 283 Byte / 1489 ms / 22708 KB)

しかしこれは簡単な Pairs ということでいいんではないか? だって同じように二重の二分探索の真ん中で線形時間の足し合わせを行っていてTLE にならないんだもん

 Ruby によるすべての提出 (AC のみ / 実行時間昇)

概ね 300 ms から 500 ms の間におさまっているから自分の 1489 ms は最も遅い部類に入るPairs を解くヒトが(Pairs の提出一覧はもちろん)ここにもあるのでは?(ったら読むわけにはいかな)

 提出 #22329595 (AC / 422 Byte / 717 ms / 22940 KB)

ープの構成は変わらないまま脳筋的努力を重ねた結果倍近く速くなったしかし 300 ms にも 500 ms にも及ばないっぱり計算量のオーダーを減らす手がどこかにあるのだろうそれがわかれば PairsAC できるぞ

 提出 #22754190 (AC / 531 Byte / 246 ms / 24136 KB)

ったど246 msRuby では僅差で一番速い

どこでオーダーが改善できる解法の根幹をなす大外の二分探索の log は欠かせない入力をなめる N もなくせないなら内部の二分探索を削るしかないのはわかってたんだけどlog を削らなければいけません「はい削りましたができるなら脳みそはいらないわけで……

トはこの問題の前に解いた射撃王にあったlog ひとつの差ってちっとした違いなんですよっと見る角度を変えるだけ……でなんとかなるなら脳みそは()

実際のところ二分探索の代わりに shift/pop を繰り返すようにしただ


261 ms の提出を読んだA 数列の値から添字を得る逆引きインデックスを事前に作成するのがキモであるようだったA の値の範囲は 10000 以下なのでそれが配列のサイズとなったところで大した大きさではない

言われてみればそうだねという感じ(だけど思いつかなかった)313 ms の提出も 328 ms の提出も 329 ms の提出も同じ下拵えをしていた

 提出 #22756111 (AC / 893 Byte / 977 ms / 30968 KB)

ったど! たまたまぶつかった別の問題ばっかり3問片付けてきたけどとうとう本丸の Pairs をクリアしたぞ! (提出日時を見ればわかるけど今日は5月の下旬なのだ日記とは)

これもやっぱり Handshake と同じように二分探索の代わりに shift/pop を繰り返すようにしたPairsHandshake と違って A 数列の値の上限が 10^9 なので逆引きインデックスを用意しておく方法は使えなかったのではないかと思う

ところでぎりぎり3桁 ms には入ったけど、759 ms には負けました配列の操作でなく添字の操作をしているところが効いてるのかな?


20210317()

最終更: 2021-03-24T16:47+0900

[AtCoder] AtCoder Beginner Contest 067D 問題 Fennec VS. Snuke

解いたあとで他の人の Ruby での解答を見たらバリエーションがいくつか見られた

 解法1:ーを2本用意してフェネックすぬけくん双方のスタト地点から各ドまでの距離を幅優先探索などで確定しそれからドの塗り分けをする

これが一番多かったと思う。公式解説に書かれている通りの手順

 解法2:1本のキーでフェネックすぬけくんが交互に陣取りをしていく

これは Ruby で最速の qib さんの提出 #20369253 (191 ms) の解法

公式解説にはこう書かれている

マス ij の距離を d(i,j) として,マス i の色は d(1,i)d(N,i) ならば黒,そうでなければ白とな結論としてマス 1 とマス N2 点から幅優先探索や深さ優先探索などを行うことで O(N) でこの問題を解くことが可能であ

解法1はたしかに解説通りの手順ではあるが解答にあたり具体的な距離まで知りたいわけではなく距離の大小関係だけ知れれば十分なのだ

解法2の手順は(スタト地点からの距離を測定する)幅優先探索に則っているのだが一見すると1手につき1マスしか塗れないゲームのルールに反しているように見えるのが難しい同じことは解法1にも言えてマス i の色は d(1,i)d(N,i) ならば黒,そうでなければ白となるが納得できるかどうかに尽きるのだけど解法2の手順がなまじゲームに似ているせいで考えてしまう

 解法3:自分の>提出 #20999230 (208 ms) やや遅くメモリ消費も多い

ェネックとすぬけくんの行動原理として想定したのは公式解説のものと同じ見立てだけが異なるどういう見立てだった

ェネック(すぬけくんでもいいが便宜上フェネックを選ぶ)のスタト地点を木の根と定めてすぬけくんのスタト地点の深さを知るすぬけくんは移動可能範囲を広げるために根に向かって移動するェネックはすぬけくんの移動可能範囲を狭めるためにすぬけくんに向かって移動する出会うのは中間の深さすぬけくんは根に向かって移動できなくなった地点を根としてその子孫ドだけを塗ることができる(だから一直線に根(ェネックのスタト地点)を目指していた)

結局のところこの問題は一本の辺を見つけ出す問題だった頂点集合をフェネック側すぬけくん側に分ける辺がどれかを見つける問題だった

その手順として幅優先探索(解法1)とその応用(解法2)と深さ優先探索(解法3)とダイクトラ法(未紹介)いろいろな方法があって実行速度の差があった同じ線形時間でも1回なめるだけで済ませられるのか2回か3回

 AtCoder Beginner Contest 148F 問題 Playing Tag on Tree

今日@2021-03-23 たまたま取り組んだこの問題が同じ方針で解けそうだった

2地点から深さ優先探索で陣取りをしていって中央付近でにらみ合ってそれからどれだけ相手陣へ侵攻(自陣へ後退)できるかを数えれば答えになりそうだった

 提出 #21207034 (WA×1 after_contest_01)

っちりと隙を見せない after_contest に撃ち落とされましたとさ

競技プログラミングをするフレンズ @kyopro_friends

ーバABC148FPlaying tag on treeafter_contestを追加したよ! 不等式に等号を入れるか入れないかを間違ってるコドが落ちるようになったはずだから確認してみてね」https://t.co/jcHP4lHFhg

Twitter
 提出 #21208328 (AC)

不等号などなかった先攻後攻を入れ替えたのと自陣へ逃げ込もうとしてうっかり中立地帯へ迷い込まないように道を塞いだ

当初方針のまま after_contest に対応したがどうにも不自然に頑張ったようなコドになってしまったこの問題に関しては想定解法通りに2通りの距離表を見比べて答えを選び出すのが良かっただろう

ところで ABC148 はオンタイムで参加していたA-D まで灰 diffE 問題に至ってもギリギリ緑という低難度回F 問題でやっと水 diff 中位だったらしい当時1時間を残していながら解けなかったのがこの F 問題何を考えて解けなかった

木の上で追いかけっこをする2人がすれ違うことができないということが認識できていなかっただから偶奇が適切な部分木を選んで逃げ込むことで追跡が躱せるような気がしていたそれじゃあこの但し書きが嘘になるのにねなおームは必ず終了することが証明できま そんなん考えたら青 diff 上位DFS Gameより難しくなるってのにね


20210313()

最終更: 2021-03-15T22:56+0900

[AtCoder] パナソニックプログラミングコンテAtCoder Beginner Contest 195F 問題 Coprime Present

本日の ABC1時間かけて ABCD の4完で残り40分考えて E 問題が解けずに終わったーム問題苦手勝ち筋とか必勝法とかっぱり見えない自分はこの先攻後攻が決まった瞬間に勝ち負けが見えるゲームをっと楽しくプレイできるんだろうなあ

本番中に E 問題が行き詰まっている最中に F 問題をタトルだけチラ見していたCoprime の単語が見えた瞬間にあきらめた別の問題だけど先々月Coprime はまた解けなかった 完全に苦手意識を持っている素数とか見たくない

 提出 #20911347 (TLE×19 / AC ×17)

割と大きめのサンプル3が通ったのでいけると思ったが TLEった

考えたことを順番に

  1. BA72があからさまな弱点
  2. AB 自体は 10^{18} になりうる大きな数なので互いに素をどのように確かめる
  3. 72 以下の素数で割ってみればいい
  4. [A,B] の区間から作ってはいけないペアが列挙できたがこれを 2^{B-A+1} 通りの組み合わせからどう除外する
  5. () ペアの左側をマージしたビト列とペアの右側をマージしたビト列を用意して2^{B-A+1} 通りを振り分けよう……実行が終わらない
  6. () ペアを併合したグループを使って解けないか……解けない
  7. 愚直にカドを1枚ずつ引いて使う場合と使わない場合で深さ優先探索を……これがさっきの TLE 提出

 提出 #20911691 (AC / 357 Byte / 221 ms / 14628 KB)

このとき(diff精進3問)解いた問題の1つABC 115 D - Christmasなんだけど素直に問題の通りに書いた最初の版が明らかに TLE を免れなくてださいけど if を使って2回の再帰呼び出しを1回に節約するパスを追加することで AC になっていた

倍倍ゲームになりうる再帰構造には特別な警戒が必要だということとそれが反転したときに改善効果が劇的だということを学んでいた今回も最後の lambda F に2行追加して AC

 () ペアを併合したグループを使って解けないか……解けない

たぶんグループの作り方が間違っていた二次ペア三次ペアと芋づる式に相互グループを作るのでなくそれぞれの数ごとに一次ペアのグループを作ってそのサイズでクラス分けをすれば計算で答えが求まったのではない計算の材料にする数字が誤っていたから求まらなかったのではないいやでもそのクラスには公倍数の情報が抜けてるのか……

 72 以下の素数で割ってみればいい

組み合わせた結果をルタリングするよりもルタリングした結果を組み合わせるべきだったのではないSQL がそうでしょう? JOIN する前に WHERE で絞るべきなんだWHERE に似ていても HAVING では遅いんだ

全探索がダメでもある種の探索が許されていたあたり今日の制約には優しさが感じられるなあ


これに関連した @kyopro_friends さんのツイトを考えていた

競技プログラミングをするフレンズ @kyopro_friends

アライグF問題はCOLOCON2018C『すぬけそだて――ごはん――の難しい版なのだ! gcd(x,y)=gcd(x-y,y)|x-y|だから72以下の素数の倍数が重複しないようにすればよくてどの素数の倍数をもう使ったかでbitDPすればいいのだ!

Twitter

gcd(x,y)=gcd(x-y,y)|x-y|ってつまり……

  • 1000010010 のように近接した2数があるときその公約数が 10000 近辺にあることはないのだなあ
  • 大小2つの数とその差(正の方)という3つの数があるときこれらは GCD を共有しているのだなあ
  • x-y を繰り返して行き着く先は x%y だけどなんだかこれってユークリドの互除法……

というような発見があったものがよく見えていない「新発見が多いークリドの互除法まで見つけてしまった開拓者か研究者に向いているのではない


20210224()

最終更: 2021-03-23T20:00+0900

[AtCoder] SOMPO HD プログラミングコンテ2021(AtCoder Beginner Contest 192)F 問題 Potion

解説を読んで ABC をコンプリトしようシリーズの1回目ABC192 で残っているのは F 問題いわゆるポーションって portion とはスペルが違ったのね

2回目があるかはわからない1回目にして解説を読んでから2日間苦しんだDPったんだけど人類が扱うには次元が高すぎるのではないかな? 自分には無理

 提出 #20468517 (AC / 614 Byte / 1451 ms / 18204 KB)

ースコドの冒頭にも引用したけど解説の要諦が次の一文

dp[i][j][k] = i 番目までから j 個選んだときの和であってmod Ck に等しいようなものの最大値

https://atcoder.jp/contests/abc192/editorial/705

自分は最初これを次のように解釈した

dp[i][j][k] = i 番目までから j 個選んだときの和であってmod jk に等しいようなものの最大値

微妙な違いがわかりますか? mod Cmod j の違いっかりミスではなく理解できる範疇を超えていたからこれってこういう意味だよねと一段次元が低い誤った理解しか生まれなかった

引き回すデータ配列の構成を教えてもらってさえ遷移が書けるまで一日かかったんだけどいざ完成したらこの微妙な勘違いのせいで時々答えが合わなかった時々答え合わせに使ったのは次のナイーブな解答スクリN30 を超えると実行時間が現実的でないので生成する入力の N は小さめにトケースはまだ利用できない

N,X,*A = $<.read.split.map(&:to_i)

p (1..N).filter_map{|c|
	k = X%c
	m = A.combination(c).map(&:sum).sort.reverse_each.find{|m| m%c == k }
	(X-m)/c if m
}.min

要するにこれを時間制限に収まるように書き直しましょうという問題だったそれが難しい

結局一度完成したと思ったスクリトを囲うようにもうひとつループを重ねた法が変わると余りは再利用できない最初から目的地(C)を定めて j を変化させなければいけないdp 配列の添字 k の上限は j でなく C である無理だよ明日にはもう自分でこの文が理解できないよ


DP であることでナイーブな解答より有利になる点は次の2つ?

  1. j+1 個の組み合わせを生成するのに j 個の組み合わせ結果が利用できる

    その際にキーとなるのが添字 i (i 番目までから j 個選んだときの和であって)j 個の組み合わせ結果を i (1N-j)によって分類しておくことでj+1 個の組み合わせを作るのに利用できる

    たぶんこれって DP のひとつの典型なんだと思うけど配列の型を示されてさえこの種の遷移(何を残して何を再利用するか)を見つけるのに1日かかった

    見つけた遷移は具体的にはjC まで増やしながらある j について i 番目の要素(A[i])i の大きい順に考えるA[i] を採用しないときに dp[j][i] に対応する C 要素の配列は dp[j][i+1] のものと同じA[i] を採用するときは dp[j-1][i+1] に記録された C 個の値と組み合わせる ij が解説とは入れ替わってら

    dp[j][i] の値を作るのに dp[j][i+1] (最内ループの直前の値)dp[j-1][i+1] (中間ループの直前の値の1要素)を再利用している

    勘違いして見えていなかったのはj=C であり j1..N の範囲で変化させる過程で各 j(C) に対応した答えが見つかる……のではなくC=1..N について j1..C の範囲で変化させなければいけないということ

  2. 組み合わせた結果の和を mod C で分類して最大の値だけを採用することで無駄な組み合わせが省ける
  3. あとはまあ組み合わせる数は多い方が初期値の点でも経時増加量の点でも有利なのでープを降順にして途中で打ち切り条件を設定したりしただけど special_xx.txt に類するケースがスペシャルな理由はこうした打ち切りが無効なところにありそう

  • 提出 #20486969 (TLE×11)

    主にイテレータを使って書き直したので遅くなるのはわかる

    Array#min の代わりに Array#[] でダイレトに最小値を取得するようにしたのでspecial_xx.txt 以外のケースでは改善している

  • 提出 #20486969 (TLE×11)

    同じように Array#min を使うのをやめたのとイテレータを使わず全て while で書いたspecial_xx.txt 以外のケースで上よりさらに少し改善しているがTLETLE

    Ruby って整数演算が足す引く剰余大小比較までどれも同じくらい遅い雰囲気演算コトに差がないなら演算子の数を減らす方がいい?

    でもどこに 800 ms も遅くなる要因があった? もう予測できない

 提出 #20581154 (AC / 615 Byte / 1437 ms / 15868 KB)

平均すると最初の提出より1割弱タイムが改善しているけど意味のある差ではない

ースはイテレータメインの提出 #20486969 (TLE×11)

ACTLE の分かれ目は4重ループの最深部にあった

  1. 初期値を正の無限大ではなく nil にした

    正の無限大は正常値として扱えるので記述が統一できるのだけどむしろ異常値として nil-1 や無限大を設定・検知してープをスキップするのが良かった

    ところで想定上限を整数で表現しようとすると 6768トが必要になる気がして採用できなかったFloat::INFINITYBignumどちらがいいともいえない打ち切り条件が ×C ではなく ÷C である理由でもある

  2. 余り(k)の計算は、k = m%ck-=c if c<=k よりも「実行されないコドが最速なのだった負の添字を使った配列参照は組み込まれた機能でありコトは支払い済みなので使い倒さなければ損になる

いくつかの C について最小公倍数で余りをとればより外側のループで DP 配列が再利用できるのではない数列 A の偏りと C の組み合わせを調べればk が取り得る値が C 種類より少なくなるのを見抜けるのではない結局のところTLE の原因はおそらく X%CA%C() がまったくマッチしないせいで4重ループを最初から最後までフル回転させられるせいだと思うから

 提出 #20639856 (AC / 870 Byte / 1167 ms / 24016 KB)

「いくつかの C について最小公倍数で余りをとればより外側のループで DP 配列が再利用できるのではないかを実装してみた話を単純にするために C が偶数の時に j=C/2; i=0DP 配列を C=C/2DP 配列として再利用した

たとえば N が上限の 100 のとき51..100 は普通に DP をする1..50 は再利用配列を使用して DP をしない限界は次の2点

  • C が大きいときの方が DP の処理量が多いので全長の下半分の節約は処理時間にすると4分の1以下の短縮効果にしかつながっていない
  • special_xx.txt 以外のテトケースでは打ち切り条件が有効に働くのでDP の節約効果が日の目を見ないどころかただオーバーヘドを増やしただけになる

 @2021-03-05トケース

ースXX (素因)A に含まれる 9999999 の数答えが見つかる C
special_01.txt52142908377193267103×4703×10764231956301
special_02.txt48620189947792921131×2719×18713×729445312
special_03.txt70227681074731923770227681074731923723
special_04.txt651020109319638361162011×231599×1735054934
special_05.txt61168850281850484182936769××737535968945
special_06.txt857415171960730822×11257×32587×11686759956
special_07.txt794433313787770441101×74910361×10500118167
special_08.txt515779426304609041101×510672699311494178
special_09.txt8962979337589569513×22769×1312161174929389
special_10.txt908429522499966622×24335153×1866496427910

N はすべて 100数列 A の要素はほとんどが 100000000から9個が 9999999 という構成

special_xx.txt が入力する数列 A の中に値の種類は1から2個しかなかったC 個選んだ和の余りがとる値は限られた 9999999 がいくつ含まれるかでしか違いが出なかったつまり1から10種類それでも C1..N の範囲で変化するうちに余りの数字(k)自体は変化していくしX%C も変化するんだけどどうやったらぎりぎり最後までマッチングしないような X が選べるんですか?