Loading...
2/9/2025
比較的簡単なセットとはいえ、6完を安定して取れていてかなり良い。この調子で維持したいな
埋め込むより言語機能に頼って順列全探索を書いたほうが速いという判断。多分そんなことはない気がしてきた。提出
use itertools::Itertools; use proconio::input; fn main() { let n = 3; input!{ a: [i32; n], } println!("{}", if a.iter() .copied() .permutations(n).any(|v| v[0]*v[1]==v[2]) { "Yes" } else { "No" }); }
集合のcontains判定なので、setに突っ込んでそれぞれ判定すればよいでしょう。提出
use std::collections::BTreeSet; use itertools::Itertools; use proconio::input; fn main() { input!{ n: usize, m: usize, a: [usize; m], } let set = BTreeSet::from_iter(a.iter().copied()); let ans = (1..=n).filter(|i| !set.contains(i)).collect_vec(); println!("{}", ans.len()); println!("{}", ans.iter().join(" ")); }
ゼッケン順に並び替えるのが厄介ですが、人はゼッケン番号を見ているので、そのように実装すればよいです。提出
use itertools::Itertools; use proconio::input; use proconio::marker::Usize1; fn main() { input!{ n: usize, p: [Usize1; n], q: [Usize1; n], } let ans = (0..n) .sorted_unstable_by_key(|i| q[*i]) .map(|i| 1+q[p[i]]) .collect_vec(); println!("{}", ans.iter().join(" ")); }
ありうる組み合わせ前通りに対して、尺取りのようなイメージで同じ値の組み合わせを探しながら確率を組み合わせればよいです。「尺取りのようなイメージ」とはつまり、ソートしてランレングス圧縮済みの2つのサイコロについて、もしであれば確率に加算してをともに1加算、であればのみ1加算,そうでなければに1加算を行います。
計算量は、あるサイコロの目が呼びされる回数は回になるので、つまり各サイコロについて個の目がちょうど回ずつ呼び出される計算になるので時間であり、この問題の制約下で十分高速です。提出
use itertools::{iproduct, Itertools}; use num_rational::Ratio; use proconio::input; fn main() { input!{ n: usize, } static N: usize = 1e5 as usize+2; let mut v = vec![]; let mut sums = vec![]; for i in 0..n { input! { k: usize, a: [u64; k], } let a = a.iter() .copied() .sorted_unstable() .dedup_with_count() .collect_vec(); sums.push(k as f64); v.push(a); } let mut ans :f64 = 0.; for i in 0..n { for j in i+1..n { let mut ii = 0; let mut ji = 0; let mut sum = 0.; while ii < v[i].len() && ji < v[j].len() { if v[i][ii].1 == v[j][ji].1 { sum += v[i][ii].0 as f64 / sums[i] * v[j][ji].0 as f64 / sums[j]; ii += 1; ji += 1; } else if v[i][ii].1 < v[j][ji].1 { ii += 1; } else { ji += 1; } } ans = ans.max(sum); } } println!("{}", ans); }
DをACした時点でEよりFのほうがAC数が多かったため、Fを先に解きました。
さて、軽く考えると次の事実に思い当たります: 「最終的な数列の番目の値はである」。これは、一番最後に挿入する(つまり、その後に挿入操作が行われない)ため、その値がそのまま適用されるため比較的すぐに気付けると思います。このような場合に、後ろから見るという手段は有効な場面が多いです1。
後ろから見るとして、番目の操作を考えましょう。番目の操作では、すでに配置済みの番目の値を無視して前から番目に配置できればよいです。これは、区間和がとなるような位置を指定すれば良く、また無視をする=更新があるので、一点更新区間和取得のSegment treeを用いることで実現できます。提出
use ac_library::{Monoid, Segtree}; use itertools::Itertools; use proconio::input; use proconio::marker::Usize1; struct Sum; impl Monoid for Sum { type S = i64; fn identity() -> Self::S { 0 } fn binary_operation(a: &Self::S, b: &Self::S) -> Self::S { *a + b } } fn main() { input!{ n: usize, p: [usize; n], } let mut seg: Segtree<Sum> = Segtree::new(n); for i in 0..n { seg.set(i, 1); } let mut ans = vec![0; n]; for (i, pi) in p.iter().enumerate().rev() { let idx = seg.max_right(0, |&sum| sum < *pi as i64); seg.set(idx, 0); ans[idx] = i+1; } println!("{}", ans.iter().join(" ")); }
全域木を構成することを考えたいです。そのために、まず「余っている辺」をpick upします。これはUnionFindを用いて連結性を確認しつつ、すでに連結済みの頂点をつなぐ辺が該当します。 余ったケーブルを用いて、初期の状態で頂点の連結成分と非連結なものとを新たに接続することを考えればよい2です。余っている辺が頂点の連結成分同士をつなぐものであれば「現時点でまだ頂点とは連結ではない頂点」と結べば良く3、反対に余っている辺が頂点とは非連結な者同士でつながっている場合は、そのどちらかの端を頂点に繋ぎ変えればよいです。この操作は正しく実装することで線形時間で行うことができるため、十分高速に解くことができます。提出
use ac_library::Dsu; use itertools::Itertools; use proconio::input; use proconio::marker::Usize1; fn main() { input!{ n: usize, m: usize, e: [(Usize1, Usize1); m], } let mut uf = Dsu::new(n); let mut amari = vec![]; for (i, (a, b)) in e.iter().enumerate() { if !uf.same(*a, *b) { uf.merge(*a, *b); } else { amari.push(i); } } // 0に集める let mut stk = vec![]; for i in 0..n { if !uf.same(0, i) { stk.push(i); } } let mut ans = vec![]; for i in amari { if stk.len() == 0 { break; } if uf.same(0, e[i].0) { while let Some(x) = stk.pop() { if !uf.same(0, x) { ans.push((i, e[i].0, x)); uf.merge(e[i].0, x); break; } } } else { ans.push((i, e[i].0, 0)); uf.merge(0, e[i].0); } } println!("{}", ans.len()); println!("{}", ans.iter().map(|(i, a, b)| format!("{} {} {}", i+1, a+1, b+1)).join(" ")); }
分からず。
はすなわちなので、そのように式変形をしておきます。ここで、以下のような言い換えを考えます。「について、集合から2数を選んで足したときの値がとなるような選び方の総数を求めよ」。この問題が高速に解ければ、元の問題も十分高速に解くことができます。4
この問題は高速フーリエ変換(FFT)を用いることで解くことができます5。
という多項式に対して、を計算した多項式のの係数こそが、集合から2数を選んでたしたときの値がとなるような選び方の個数になります。ただし、これは重複があることと、同じ値(例えばに対して)を選んでしまう場合があるので、このようなものを省きながら足し合わせることで答えを得ることができます。提出
use ac_library::convolution_i64; use proconio::input; fn main() { input!{ n: usize, s: [usize; n], } static N: usize = 1_000_000 + 2; let mut a = vec![0; N]; for &si in &s { a[si] = 1; } let conv = convolution_i64(&a, &a); let ans = s.iter().map(|&si| (conv[2*si]-1)/2).sum::<i64>(); println!("{}", ans); }