ABC419

久しぶりに酷い負け方をした。A~Dの4完で、Eで思考ロックに陥ってしまって嘘だろうなと思いながらその解法を捨てられなかった。かなしい。
A - AtCoder Language
この手の場合分けは2個ぐらいで済むならif-elseを使ったほうが楽がちですが、これぐらいの量になるとmatch式を使うときれいに書けていいですね。Unknownの綴りが不安でめちゃくちゃ丁寧に確認しましたが、よく考えるとコピペで良かった。 提出
use library::utils::input::Input;
fn solve(ip: &mut Input) {
let s = ip.next::<String>();
let ans = match s.as_str() {
"red" => "SSS",
"blue" => "FFF",
"green" => "MMM",
_ => "Unknown",
};
println!("{}", ans);
}
B - Get Min
Bなのでだいたい何やっても良いシリーズです。multisetを使いましたが、解説はpriority queueなんですね~。確かに。提出
use library::{data_structure::multiset::MultiSet, utils::input::Input};
fn solve(ip: &mut Input) {
let mut set = MultiSet::new();
let q = ip.next::<usize>();
for _ in 0..q {
let t = ip.next::<usize>();
if t == 1 {
let x = ip.next::<u64>();
set.add(x, 1);
} else {
if let Some((&k, &_)) = set.min_key() {
set.remove(k, 1);
println!("{}", k);
}
}
}
}
C - King's Summit
ちょっと難しい?8近傍に動けるので、集合地点をとすると人は集合するのに時間かかります。答えはこれの最大値、つまりですから、この値を最小にするようなの組がどのようになるか?を考えればよいです。
これはすでにx座標とy座標に分離できていて、それぞれについて考えればよいです。どこに集合地点を置くか?と考えると、どう考えてものそれぞれ最大値と最小値の平均(つまり、数直線上で一番左と一番右の、ちょうど真ん中)に置くのが良いですね。
この問題の設定では平均が小数になるのでどちらを選ぶ~みたいなことを気にする必要もなく、決めたを使って冒頭の式を愚直に計算すれば時間でこの問題が解けます。提出
use library::utils::input::Input;
fn solve(ip: &mut Input) {
let n = ip.next::<usize>();
let v = (0..n).map(|_|ip.pair::<u64,u64>()).collect::<Vec<_>>();
let x = v.iter().map(|v| v.0).collect::<Vec<_>>();
let y = v.iter().map(|v| v.1).collect::<Vec<_>>();
let px = (*x.iter().max().unwrap() + *x.iter().min().unwrap()) / 2;
let py = (*y.iter().max().unwrap() + *y.iter().min().unwrap()) / 2;
let ans = v.iter()
.map(|v| v.0.abs_diff(px).max(v.1.abs_diff(py)))
.max()
.unwrap();
println!("{}", ans);
}
始め8近傍ではなく4近傍で動くものだと思っており、「難しくないですか?」と唸っていました。誤読には気をつけよう。
D - Substr Swap
これは簡単。文字のインデックスが変わることはなくて、位置の文字が元通りか逆転したかがわかればよいです。また、操作された回数が偶数回なら元通り、奇数回なら逆転です。
よって、問題は区間への加算ですが、これはimos法で出来ます。ということで、解けました。提出
use library::utils::input::Input;
fn solve(ip: &mut Input) {
let (n, m) = ip.pair::<usize,usize>();
let s = ip.next::<String>().chars().collect::<Vec<_>>();
let t = ip.next::<String>().chars().collect::<Vec<_>>();
let mut imos = vec![0i64; n+1];
for _ in 0..m {
let (l, r) = ip.pair::<usize, usize>();
imos[l-1] += 1;
imos[r] -= 1;
}
let mut ans = vec![];
let mut cur = 0;
for (i, imos_i) in imos[..n].iter().enumerate() {
cur += *imos_i;
if cur % 2 == 0 {
ans.push(s[i]);
} else {
ans.push(t[i]);
}
}
println!("{}", ans.iter().collect::<String>());
}
E - Subarray Sum Divisibility(upsolve)
難しい。人類は通し過ぎだと思います。
取り敢えず、の長さの連続部分列に興味があるので、これを定義することにします。に対して、=とします。
ここで、との差分に注目します。まず、です。また、はそれぞれで割ったあまりがですから、その差分であるもで割ったあまりがになる必要があります。よって、とがで割ったあまりが同じであればよいです。
以上から、 インデックスをで割ったあまりがであるようなの値をで割ったあまりがにするのに必要な最小コスト、のようにするような動的計画法で、この問題を解くことが出来ます。提出
use library::utils::{chlibs::ChLibs, consts::INF, input::Input};
fn solve(ip: &mut Input) {
let (n, m, l) = ip.triple::<usize, usize, usize>();
let a = ip.vector::<u64>(n);
let mut dp = vec![INF; m];
dp[0] = 0;
for i in 0..l {
let mut ndp = vec![INF; m];
let costs = (0..m).map(|k| {
a.iter()
.skip(i)
.step_by(l)
.map(|ai| if k as u64 >= *ai { k as u64 - *ai} else { k as u64 + (m as u64 - *ai) })
.sum::<u64>()
})
.collect::<Vec<_>>();
for j in 0..m {
if dp[j] == INF { continue; }
for k in 0..m {
ndp[(j+k)%m].chmin(dp[j] + costs[k]);
}
}
dp = ndp;
}
println!("{}", dp[0]);
}
F - All Included(upsolve)
まず最初に思いつく(思いつきたい)のが、文字目まで見て、集合の文字列を作成済みで、今完成した文字列がであるものの個数、とする動的計画法です。ただ、これだけでは不十分で、というのは「今完成した文字列」は個あるためテーブルが足りません。しかし、実際に知りたいのは文字列が完成したかどうかだけで、これは「に含まれる文字列のprefix」だけを持っておけば十分です。
そして、遷移をするのにAho-Corasick法を用いることができるらしいです。Aho-Corasick法そのものは、文字列の集合があって、の要素が与えられた文字列の部分文字列であるか1を高速に判定できるアルゴリズムですが、この問題のsuffixに関する遷移がAho-Corasick法における遷移と一致する2ため遷移先を高速に求めるために利用できます。
少し理解が曖昧なままなのでこれ以上は書かないでおく。とにかく、解けはした。要復習だけど...。提出
use ac_library::ModInt998244353 as Mint;
use library::{algorithm::aho_corasick::AhoCorasick, utils::input::Input};
fn solve(ip: &mut Input) {
let (n, l) = ip.pair::<usize, usize>();
let s = (0..n).map(|_| ip.next::<String>()).collect::<Vec<_>>();
let ahocora = AhoCorasick::new(&s);
let m = ahocora.node_size();
let mut dp = vec![vec![Mint::new(0); m]; 1<<n];
dp[0][0] = Mint::new(1);
for _i in 0..l {
let mut ndp = vec![vec![Mint::new(0); m]; 1<<n];
for st in 0..1<<n {
for j in 0..m {
let next_ids = ahocora.destination_node_ids_from_id(j);
for id in next_ids {
let outputs = ahocora.nodes[id].outputs.iter()
.map(|i| 1<<i)
.sum::<usize>();
ndp[st | outputs][id] += dp[st][j];
}
}
}
dp = ndp;
}
println!("{}", dp[(1<<n)-1].iter().sum::<Mint>());
}