AtCoder Beginner Contest 196 F - Substring 2
そのままSとTをコンボリューションすると下図のようになる
配列の4番目には、S[3]*T[1] + S[2]*T[2]*S[1]*T[3]
配列の5番目には、S[4]*T[1] + S[3]*T[2]*S[2]*T[3]
配列の6番目には、S[5]*T[1] + S[4]*T[2]*S[3]*T[3]
が入っている
これが仮に
配列の4番目には、S[1]*T[1] + S[2]*T[2]*S[3]*T[3]
配列の5番目には、S[2]*T[1] + S[3]*T[2]*S[4]*T[3]
配列の6番目には、S[3]*T[1] + S[4]*T[2]*S[5]*T[3]
であれば、配列の4番目にはS[1..3]とT[1..3]で両方とも1みたいな数値が入っている。
SかTのどっちかを反転する。
convolutionをとる(両方とも1みたいな数値を計算)
SとTの配列の中身をすべて反転する
convolutionをとる(両方とも0みたいな数値を計算)
で答えが出る。
解説見た 難しかった。慣れてたらいけるかも?
第七回 アルゴリズム実技検定 O - コンピュータ
公式解説とか正解コードとかブログとかいっぱい見た
dp[i]= i日目に必要なコンピュータを手に入れるまでに必要な最小の金額
というdpテーブルを作ることを考える
dp[0] = 0にしておく
dp[i] = min(dp[j]+B[i] | B[i] <= ( Σ(0~j)A[k] - dp[j]) )
後ろを全部調べるとO(n^2)になるので、
B[i] <= ( Σ(0~j)A[k] - dp[j]) ←この条件を満たす遷移をmin-outヒープで、
dp[j] ←上のヒープに入っている遷移の中で最小をc++のmultisetで出す
dp[X]を計算した後
ヒープに、 ( Σ(0~X)A[k] - dp[X], dp[X] )
multisetに、(dp[X] , Σ(0~X)A[k] - dp[X] )
と、(a, b) (b, a)みたいなタプルを入れる
dp[X+1]を計算する時、
ヒープ中のタプルで,これを(a, b)としたときに、a<B[X+1]なものを取り除き、multisetから(b, a)を取り除く。
dp[X+1] = B[X+1] + (multiset中最小の要素の第一成分)
と更新できる。
むずい
fn solve(){
let sssss = std::io::stdin();
let mut sc = Scanner { stdin: sssss.lock() };
let mut N:usize =sc.read();
let mut A = vec![0i64;N+1];
let mut B = vec![0i64;N];
for i in 0..N{
A[i+1] = sc.read();
B[i] = sc.read();
A[i+1]+=A[i];
}
let mut h = BinaryHeap::new();
let mut m = MultiSet::new();
let mut ma = -1;
h.push*1;
m.insert*2;
let mut dp = vec![0;N+2];
for i in 0..N{
if B[i]<=ma{
B[i] = ma;
}
else{
ma = B[i];
}
while(h.len() != 0){
let mut t = h.pop().unwrap();
if -(t.0)<B[i]{
m.remove(&(t.1, -t.0));
}
else{
h.push(t);
break;
}
}
if m.set.len() == 0{
println!("-1");
return;
}
let mut mm = m.get_min().unwrap();
dp[i+1] = B[i]+mm.0;
if i != N-1{
h.push*3;
m.insert*4;
}
}
println!("{}", A[N]-dp[N]);
}
典型90問 073 - We Need Both a and b(★5)
グラフDPをする
dp[i][j] = (i=0 なら、jが所属している連結成分はaのみで出来ていて、それ以外の連結成分はaとbで出来ている
i=1なら、jが所属している連結成分はbのみで〜〜〜
i=2なら、jが所属している連結成分はaとbから〜〜〜
今見ているノードがaの時、状態の全体(i=0 or i=1 or i=2)は
子がa:
エッジを引く ー> 子の状態が、i=0 or i = 1 or i = 2
エッジを引かない ー> 子の状態が、i=2 (子の状態がi=0 or i=1のときにエッジを引かないと、すべての連結成分がaとbを含むという制約を満たさない)
全部でdp[0]+dp[1]+dp[2]*2
この内、親の所属している連結成分にaのみ含まれるようになる状態は
dp[0] +dp[2] (エッジを引いたときに子の状態がi=0と、引かないときのi=2)
子がb:
エッジを引く 子の状態が、i=0 or i = 1 or i = 2
エッジを引かない 子の状態が、i=2 (子の状態がi=0 or i=1のときにエッジを引かないと、すべての連結成分がaとbを含むという制約を満たさない)
全部でdp[0]+dp[1]+dp[2]*2
この内、親の所属している連結成分にaのみ含まれるようになる状態は
dp[2]
今見ているノードがbの時、〜〜〜
全部からaのみになるやつを引くと、aとbを含むやつが計算できる
aとbを含む奴を計算して、全体から引いてaのみを計算するようにしてみる
v0 = 今見ている部分木が、aかbどっちかしか含まないようになる or 両方含むようになる パターン
v1 = aかbどっちかしか含まないようになる パターン
v0-v1が i=2の答え
- fn dfs(s:usize, bef:usize, g:&Vec<Vec<usize>>, dp:&mut Vec<Vec<usize>>, c:&Vec<char>){
- let mut all = 1;
- let mut v0 = 1;
- let mut v1 = 1;
- for i in &g[s]{
- if *i == bef{
- continue;
- }
- dfs(*i, s, g, dp, c);
- if c[s] == 'a'{
- if c[*i] == 'a'{
- all*=(dp[0][*i]+dp[2][*i]*2%MODu)%MODu;
- all%=MODu;
- v0 *= (dp[0][*i]+dp[2][*i]*2%MODu)%MODu;
- v0 %= MODu;
- v1 *= (dp[0][*i]+dp[2][*i])%MODu;
- v1 %=MODu;
- }
- else{
- all*=(dp[1][*i]+dp[2][*i]*2%MODu)%MODu;
- all%=MODu;
- v0 *= (dp[1][*i] + dp[2][*i]*2%MODu)%MODu;
- v0 %= MODu;
- v1 *=dp[2][*i];
- v1 %=MODu;
- }
- }
- else{
- if c[*i] == 'a'{
- all*=(dp[0][*i]+dp[2][*i]*2%MODu)%MODu;
- all%=MODu;
- v0 *= (dp[0][*i] + dp[2][*i]*2)%MODu;
- v0 %= MODu;
- v1 *=dp[2][*i];
- v1 %=MODu;
- }
- else{
- all*=(dp[1][*i]+dp[2][*i]*2%MODu)%MODu;
- all%=MODu;
- v0 *= (dp[1][*i]+dp[2][*i]*2)%MODu;
- v0 %= MODu;
- v1 *= (dp[1][*i]+dp[2][*i])%MODu;
- v1 %=MODu;
- }
- }
- }
難しかった すごく
CF726 D. Deleting Divisors
実験してると、nが奇数だと先手は必ず負ける。
これは、nが奇数で先手が約数pを選ぶとと、n=p^x * q^y * r^z => n-p = p( p^(x-1) * q^y * r^z - 1 )
n-pは 奇数*偶数 のかたちにかける。
後手は奇数を選べば 奇数(偶数 - 1 )を先手に渡せる。これは奇数。
最終的には先手に素数を渡すことになって、後手勝ち。
nが偶数の場合、2^m * p
後手に奇数を渡せば先手勝ちなので、奇数を選んで先手勝ち。
ただし、p=1の場合は奇数の約数がないので偶数を選ぶことになるが、
2^x * p (p != 1) の形で渡すと渡した方は負けになるので、そうならないよう(2^m) / 2
の形で渡すのが最善。これはmの偶奇で勝敗が決まる
Editorialと日本語の落ちてた解説見てやっと理解できた
すごく勉強になった
CF 722 E問題 Trees of Tranquillity
まずKeshiのグラフ上でオイラーツアーをして、出発した時間と帰ってきた時間を記録する
これを使うと木の上で2つのノードが親子関係にあるかどうか調べられる
[出発した時間, 帰ってきた時間]の区間が図1のような関係になっている時親子関係がある。
次にSoroushのグラフ上で,BTreeSetを持ちながらdfsをする。
BtreeSetには( ノードxを出発した時間, x)というタプルが入る。
あるノードvを訪れたとする。
BTreeSet上で二分探索して、(ノードAを出発した時間, A) (ノードBを出発した時間, B)
ノードAを出発した時間<ノードvを出発した時間<ノードBを出発した時間 となったとする。
図1の場合はノードAを削除してノードvを入れたほうがお得
図2の場合はノードvを入れれる
図3の場合はノードvを入れないほうがお得
図4の場合は入れれる
図2,図4が成立する時入れれる
dfs中に最もBTreeSetの要素数の最大値が答えになる。
勉強になるけど解説見てもわからないぐらい難しかった。
でも楽しい問題だった。
コンテスト中に解けたら凄いと思った。