unsolved

uwiさん等を見習ってここにメモります

OpenCup
FHC

2016r2

SRM

SRM705 hard 線形代数
SRM706 hard
SRM707
SRM708
SRM709
SRM710

メモ

知らなかったできることのメモとか

1e18+3(codeではll 1LLe18+3)は素数
inv(1e9+7) mod(1<<64) = 13499267949257065399ULL ←これよく考えたら普通にオーバーフローさせながら計算すればよかった
DAGにpathを何個書けば頂点が覆えるか->bipartite matching
和がnになるような1以上の整数達がいる時、種類は√2nくらい (よく考えたら自明だった)
負辺min_cost_flowははじめのポテンシャルだけBellmanFord
木DPでstackoverflow->先にbfsでtopological orderをとる
二人ゲームで手番数が決まっていて相手の手をキャンセルできる手が打てる場合のmin-maxは手番数kとするとk=1,2しか考えなくて良い
挿入,削除ありのk番目の値→setで先頭k個を持つ
Z-algorithmでs[i~]とTのpfx が全部求められる
1文字違い→前からの一致+後ろからの一致=N-1
stack→区間DP
幾何で角度区間のマージ←ベクトルでやると楽かも(ただしもとより180度以上ずれてるとまずそう)
n次関数を足す→差分を取る(BITのあれみたいなのもあるけど,そもそも持つ値を差分にする も有用)
平面上4方向は45度回転で独立なx+y軸,x-y軸になる
数列で大小関係みたいな問題は平面上の点だと思うことが多い そうじゃなくてもdpとかを視覚的に表す方法があるならそれを考えるのもあり(というか図形に落として考えるのが苦手すぎる)(ex.LIS)
tの上限が1e9とかで答えがtの多項式であることがわかってるなら,小さいtについて求めて多項式補間もありうる(数え上げで結構ありそう)(わかんなければ実験も良さそう)
全部がうまく行ってるか みたいなのをクエリごとにはやく判定するためには,何個うまく行っているかを持って更新する 例えば後退解析とかでも,そこに行ける状態の内何個を処理したか を持っておき,それが全部になったらやる、みたいな
DPをデータ構造で高速化するのは貰うDPがいい(範囲のsumやmaxを一気に取りたいんだから).
dp[i]からdp[i+1]への遷移を何も考えずにsegtreeに載せると、区間を意識しなおさなくても連続部分列に対するクエリに応えられる.
連続なものはとりあえず離散的な数え上げ+簡単な積分 に落とす,たとえば∫max(a,min(b,c)) みたいなのはa,b,cの大小関係を決めたものをすべて考える.
こうするとuniform distribution[0,1]からN個独立に取ってきた時のi番目(1-indexed)の値の期待値は? になって、式を描くとベータ関数になることがわかりi/(N+1)になる. (i+1番目)-(i番目) がiによらないので1/(N+1)になることを考えるとわかりやすい.
遷移する順番が関係ないDP→
なんらかの順序でソートすると一部のキーを消せることが多い.あとcount系なら戻せる.
あとこれは最短路問題に落とせて、例えば常にコスト1でbfsになるなら計算量は変わらない.
しかし、「状態を一部に制限しても最短経路が存在する」しかしdpの順としてはこのようなものを実現するうまい順は存在しない,というような状況だと、bfsの方が計算量が良くなる場合がある.
ex:絶対値がK以下の整数がある、何個足せば0にできますか→DPだとO(K^2)状態になるが、bfsだとO(K)状態.

CodeFestival2017 qualB F問題

F: Largest Smallest Cyclic Shift - CODE FESTIVAL 2017 qual B | AtCoder

この問題のおもしろい解法についてです。

解法1

文字列の集合を持って、「最小と最大を取り出し、この順に並べたものを新たに追加する」をやって最後に残ったものが答え.初期状態は"a"がA個,"b"がB個,"c"がC個.


解法1の正当性を証明するために、まずは次の解法を考えます.

解法2

再帰的な解法.
string solve(int A,int B,int C,string a,string b,string c) という関数を考える.
これは、文字列aを文字'a'だと思い込んで、(b,cも同様)問題を解いた時の最終的な答えを返す関数とします. なので元の問題の答えはsolve(A,B,C,"a","b","c").

内部では次のことをやります.

自明なケース

i) B+C==0 の場合
明らかにaをA個並べたものを返すしかありません.
ii) A==0 の場合
solve(B,C,0,b,c,"")を返します

まともなケース
int q = A/(B+C);
int r = A%(B+C);
string o = a*q;
if(r<=C){
	return solve(r,B,C-r,o+a+c,o+b,o+c);
}else{
	return solve(r-C,C,B-(r-C),o+a+b,o+a+c,o+b);
}

(string * int は string を int 回連結したものとします)

たとえば,
solve(6,3,1,"a","b","c") では、
solve(1,1,2,"aab","aac","ab")が内部で呼ばれ、

solve(3,3,4,"a","b","c") では、
solve("ac","b","c",3,3,1)が内部で呼ばれます.

これでちゃんとsolveが計算できていることを示します.自明なケースに関しては明らかなので、まともなケースについて考えます.

まず非自明なケースのsolveの引数a,b,c(文字列)は、次の条件†を満たします(これは再帰的に簡単にわかります *1 ).

†:どの文字列も,他のどの文字列のprefixではない.

この条件を満たす文字列集合Sに対して、一般に次のことが成り立ちます:


Sを辞書順ソート済みとしてS = {w_1,w_2,..,w_N} とする.この時、
a := w_i1 + w_i2 + .. + w_iI
b := w_j1 + w_j2 + .. + w_jJ
と置く(文字列の連結)と、
a < b ⇔ (i1,i2,..,iI) < (j1,j2,..,jJ) (添字の辞書順)

これは、†を満たしていることから、i1 ≠ j1 ならその時点でaとbのどちらが小さいかわかる ということを考えるとわかります.

これはすなわち、Sの文字列を新たに一つの文字とみなして大小比較しても同じ結果が得られることを指しています.

元の問題に戻ると、これによってsolveを再帰的に呼ぶ時に常にa,b,cを一つの文字とみなしても元の問題が解けていることがわかります.

コードの内容に戻ると、まずaをできるだけ均等に(q+1がr個,qがB+C-r個)分割しています.こうすべきなのは、aの数が連続している部分の最長の長さがより長いとfの値が小さくなってしまうことからわかります(追記:‡不十分で、aaa,aaa,a より aaa,aa,aa の方がよいことを示していない あとで説明します).

なのであとは、さっきあげた例A=6,B=3,C=1でいくと、aa?,aa?,a?,a? (の?に適切にb,cを入れて,)並び替えてfを最小化するのが目的になります.

もしaaの後に全てcを置けるなら、そうでない状態よりfは大きくなる(aabから始まる文字列がなくなる)ので、そうすべきです.すると次の文字列はaac,ab,acしかなくなるのでこれで再起します(コード中のifの方です)

そうでない場合でも(elseの方),実はできるだけaaの後ろにcを詰め込んだほうがよいです(※).すると,次の文字列はaac,aab,abしかなくなるのでこれで再起します.

※の理由:
出てきうる文字列aab,aac,ab,ac は 条件†を満たすので、1,2,3,4と置き直します.

「1,2,3,4からなる文字列sがあって、1と4が共存する場合、ある1と4が存在して、その1を2に、その4を3に書き換えることでf(s)をより大きくできる」
ことを示せば十分.

まずsの4をどれでもいいから一つ固定します.そこから(cyclicに)左に見ていってはじめに見つけた1を固定します.この4,1を3,2にすることを考えます.この文字列をtとおきます.

まず変更の結果tに1がなくなるケースでは、明らかにfは大きくなっています(f(s)は1から、f(t)は少なくとも2からはじまるので)

そうでない場合、
sのcyclic shift の内1からはじまるものの集合を考えると、これはf(s)を含みます. これらの文字列s'においては、さっき固定した1と4の間で切れることはありえません(つまり、1と4はs'にこの順に出てきます)

これらの候補に対応する(同じ分だけshiftした) t の文字列達を考えます.この集合は必ずf(t)を含みます.(tには1があり、かつ1からはじまるものをすべて含むため)

それぞれの対応するペア(s',t'と描きます)に対して、s' < t' を示します.
これが示せれば、f(t) > それに対応するs' ≥ f(s) となるのでOKです.

(s' < t' の証明)
さっきの太字の部分で主張したことから、
s' = ..1..4..
t' = ..2..3..
となるので、OK(おわり)

(※の理由おわり)

よってできるだけcをaa?に詰め込んだほうがいいことがわかったので、解法2の正当性も終わりです.

‡(aを均等にすべきな理由):
上の証明と似たような感じで、b,c,ab,ac,aab,aac,..は条件†を満たすので、aの数が偏っている(2以上違う)時は、その二つを固定してaの個数を平均化するのがいいことがわかる.

解法1の正当性

途中状態でsolve()の再帰と同じ状態になることがわかるので解法2と同じ出力になることがわかる.(ベースケースは明らか)


ちゃんと詰めたつもりなんですが、おかしいところがあったら教えてください.

*1:細かいですが、自明なケースiiによって実は満たさなくなるんですが、まあ本質的ではないです,例えば新たな引数をランダム文字列にしても良いので

JAG 2017 day1 (snukeセット)

Tasks - Japan Alumni Group Summer Camp 2017 Day 1 | AtCoder


A:しりとり
適当なオイラー閉路を考えてその途中までを取るとか考えると正当性がわかりやすい

B:リス
ちょっと迷走仕掛けたが、「最終的な場所」で考えればいいことがわかる

C:すごろく
動的FFTみたいなやつ(segtreeっぽい といってもいいかも)
左からDPを埋めるとして、ある区間を正しく計算する関数calc(l,r) を用意する.
calc(l,r)では、分割統治っぽく、まずcalc(l,m)を呼んで、次に[l,m) から[m,r) への全ての遷移をFFTでやって、最後にcalc(m,r)分を追加する とやればよい.

コンテスト中は面に出てくる値の種類が多い時は速く終わるからモンテカルロ とかやってました(カス)

D:くさかべ
ま、幾何ライブラリ写経したんですけどね(ここらへんもはやタイトルが全く関係ない)

E:ベクトル式
BNFを書き換えたほうがいい みたいな問題はたまにあるので、これLLじゃないよ~とか思ったら考えたほうがいいと思います

F:逆から見てにぶたんすると片方向きだけでいい.左から貪欲に取ると右方向には極小(そ)

G:2リストらしい.頑張ってグラフを作ると一般の場合でも解けるが、H=2だとサイクルができちゃうケースが限られているのでそれだけやればOK

H:

過半数がどうこう、よりまず自由度3で条件2だから一つ決めればすべて決まるというのに気づくのが重要

イベルタルってこんな動き方するんですか?(いいえ)

I:
フローに必要な辺 のやつ
正当性は簡単.
あるフローFを固定する.Fの残余グラフの(有向)サイクルに含まれない→確定(Fに含まれてる⇔かならず使う,含まれてない⇔必ず使わない)
含まれてるならそれにそって流せばその辺は消せるので、どっちもありうる.
x→y があるサイクルに含まれている ⇔ sccでa,bが同じ成分 なので余裕

J:
2WA(ha?)

K:パンプキン
一応式を書くとa+bがデカい順でOK.

ちゃんと「ん」で終わってしりとりが終了している.
最近始めたので読解がすんなり出来た.裁きの光ってカードが好きです.

yukicoder ☆5,6

何故か埋めています。解法メモなのでネタバレ注意。

☆5

42:
63:
93:

変なことをすると下から挿入できるが、普通に包除

114:
137:

特異問題です。これはむずい
1,x,x^2,..なら順番に倍数だから、modでピッタシになるようにdpするっていうのが出来たわけだが、今回はxを自由に使えるっていうのをx,2x,4x,..が1枚ずつあると言い換える。それでx_1,..,x_N,2x_1,..,2x_N,..っていう順番で使うかどうか決めていけば、各状態ではSum x_N 通りくらいしか持たなくて良い。
自由DPのときに分割するのは典型っぽいがその後こんなうまくいくのはびっくりしたなあ

214:
234:
243:
248:
263:
271:
283:

カス

284:

異なる二種類の和のmax みたいなのは 上位2種類持っておけば良い Bicycles(蝶々のやつ)とか

295:

まずこういうふうに並べるのが最適なのを示すのがアレだが、無駄に分割するのは意味がない(すべての文字でマージすれば絶対に積が大きくなる)のでOK.
あとはすごく小さいケースだけ場合分け
max制限がある数え上げでパラメーターtがあってn^tオーダーとかだとtが1,2,3とかのケースだけやればいいみたいなのあるよね

377:

Burnsideのいい練習問題だと思う.
不変な(x,y)の集合 みたいなのを考えたくなるが、ちゃんとBurnsideがわかってれば G = Z/HZ * Z/WZ の各元ごとにかんがえればいいことはわかる

382:

最大独立点集合, 乱択おもったより強いなあ(まあ乱数ガバいからまともに作ったケースだと落とされるのかな)

426:

苦行segtree

435:

無.なぜかmod 9 の世界でかけたり割ったりする時に 3^a * b (b = 1,2) の形で持っていたが,bは1,2,4,5,7,8なんだよなあ. 気をつけます

465:
474:

一時期ちょっと流行ったmod2系. Lucasの定理の拡張でmod素べきとかもあるので、困ったら使ってもいいかも(まあ愚直にやれるなら↑435みたいにやればよい).

540:

予想投稿サイトやめろ

UTPC2011

D:探索ゲーでpair<pair<int,int>,pair<int,int>>とかやるのつらいので、tupleを使う.勝手に辞書順が入ってるのでsetに入れられる

	using State = tuple<int,int,int,int>;
	queue<State> que;
	que.push({1,2,3,4});
	int a,b,c,d;
	tie(a,b,c,d) = que.front();
	cout<<a<<b<<c<<d<<endl;

E:
長さNの数列a,bがある、indexのリストを選んで、(prefix sum of a) ≤ bとなるようにせよ.という問題

まずどの順に選べばいいかが先にわかるというのが典型テク.連続する二つのswapを比べるとこの場合はbが小さい順がいいことがわかる.

なので今何個選んだかのDPで出来るんですけど、実はO(NlogN)になるらしい.

b昇順にするのまでは一緒、とりあえず貪欲に選ぶことにしていって、選んだ結果超えちゃったら、その時点でbが一番でかいやつを諦める(b昇順であることから諦めるのは高々一回であることがわかる).
証明:hoge

F:構成.帰納法

G:長さ選んで三角形→連続するように詰めれる

H:キャッシュ戦略→MCF.各sに対して[s,s+1)か[s,t)のどっちを選ぶか決められるんだけど、はじめ全部ちっちゃい方を選んでいると思うと、[s+1,t)を選んで重なりがf-1個 とみなせる,かなり特殊っぽい

I:(x==3)みたいな式が書きたいんだけど、これは(x==4)みたいな2冪にたいしてできれば良くて、でも冷静に考えるとこれはできないことがわかる,ちゃんというと、xの上の桁がyの下の桁に影響をおよぼすことは絶対にない.
なので出力のi桁目を決めるときは入力の0~i桁目を使えば良くて、(x==3?8:0)みたいなのは簡単にかけるのでOK.

J:まずN!通りのうちの数え上げになる.優先度順に見れば単純に二分木に挿入するだけになる.根が何番目か決めることでO(N^3)のDPができる.更新部分が畳み込みなのでO(N^2logN)にできる.hがでかいとこはどうせほぼ0なので、O(Nlog^2N)にできる.double版多項式まともに作ってなかった・・・

K:M≤N+500 圧縮です

L:永続

SRM710

難しすぎる. 0完で-100くらいくらって黄色になった.

easy:
AとBは逆操作.なのである標準形みたいなのを設定してそこに持っていけばいい.標準形としてはぜんぶおなじとこに集まってるのとかを選べば良くて、これに持っていくにはAを選びまくれば良い.

操作が二種類ある時は、それを組み合わせて一般的で簡単な操作を作れないか(石を1つ動かすとか), 実は実質一種類の操作に落とせないか(ex.f(x) = 4x+3, g(x) = 8x+7), 逆操作になってないか などを疑いましょう。


med:
こんなの実験しないと絶対に解けません。
考えると、magicを無視した状態で、「xorが0 もしくは全ての山が1」の盤面にしたら負け というのがbase stepとしてわかる.
あとは頑張って実験をすると、
その状態で次に動かすプレイヤーの敗北条件は,
{ \displaystyle
\begin{cases}
  \text{ if } \exists i , x_i>=4 & \bigoplus_{i=0}^{n-1}x_i=1 \\ 
  \text{ otherwise } & \bigoplus_{i=0}^{n-1}x_i=2
\end{cases}
}
であることがわかるので、これを計算して終わり。
証明:
ifの方を①,otherwiseの方を②とする.
勝ち状態で回ってきたときに①→①,②→②,①→② のどれかを使って負け状態を回せることと、
負け状態で回ってきたときに①→①,②→②,①→② のどれを使っても勝ち状態を回すことになることを示せば良い.
①→①はxorを0にすると負けなので1→2以上→1→2以上→・・・と遷移することからわかる
②→②は,
xor=1のとき, 全1でないことから2,3があり、しかも2以上は2個以上ある.なので3→0か2→1をしてよい
xor=3のとき,同様に2か3がある, 3があるときは3->2, ないときは2があり、かつ1もあるので1→0 でよい
xor=2のとき(負け), 1か3にしか遷移できないのはそう.
①→②は
勝つ側:4以上がひとつ→なしとなるので、事前状態はxor4以上.なのでこれを使ってxorを2にして渡せる.
負ける側:xor=1で回ってきているので4以上がひとつであることはありえない.

いや示せって言われたら示せるけどこんな場合分け実験せずに出来るわけないよね.

hard:
包除パートが見えるので、簡単やんけ!って思ったら前半の列挙パートが本質だった(座標の値が一致しても良いことに注意)
長さが正のintervalの端点の順序としてあり得るものは、A055203 - OEISで、N=6で既にデカいので全列挙は不可能.
なので左から頑張っておいていくDPをすごく頑張って書くと通る.
はじめどうせ求めるものは座標の値が一致しないものなんだからこの前提で包除すればええやん!って思ったけどそうではありませんね?
包除するときどういう形で求めているかちゃんと考えましょう