結果
| 問題 |
No.3350 Tree and Two Apples
|
| コンテスト | |
| ユーザー |
Solalyth
|
| 提出日時 | 2025-11-13 17:15:25 |
| 言語 | Rust (1.83.0 + proconio) |
| 結果 |
AC
|
| 実行時間 | 160 ms / 2,000 ms |
| コード長 | 8,707 bytes |
| コンパイル時間 | 13,927 ms |
| コンパイル使用メモリ | 399,512 KB |
| 実行使用メモリ | 28,004 KB |
| 最終ジャッジ日時 | 2025-11-13 21:29:36 |
| 合計ジャッジ時間 | 19,407 ms |
|
ジャッジサーバーID (参考情報) |
judge2 / judge1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 1 |
| other | AC * 35 |
ソースコード
#![allow(unused_must_use, non_snake_case, unused_labels, unused_imports, non_upper_case_globals)]
use external::*;
use cplib::prelude::*;
use nest as vec;
const INTERACTIVE: bool = false;
// const INTERACTIVE: bool = true; use input_interactive as input;
fn solve() {
input! {
N: usize, UV: [(usize1, usize1); N-1]
}
const M: u64 = 998244353;
// 線形合同法による疑似乱数
let mut lcg = std::iter::successors(Some((230183, 13182938)), |&(x, y)| {
Some(((x*30253231+230183) % M, (y*23018237+2018239) % M))
});
let mut pool = HashMap::new();
let mut f = |e| {
*pool.entry(e).or_insert_with(|| {
lcg.next().unwrap()
})
};
let mut E = vec![void; N];
for &(u, v) in &UV {
E[u].push(v);
E[v].push(u);
}
let mut par = vec![0; N];
let mut order = vec![0];
for i in 0..N {
let i = order[i];
for &j in &E[i] {
if j == par[i] { continue; }
par[j] = i;
order.push(j);
}
}
let mut up = vec![(0, 0); N];
let mut up2 = vec![0; N];
let mut set = vec![];
for &i in order[1..].iter().rev() {
for &j in &E[i] {
if j == par[i] { continue; }
up[i] = (up[i].0 + up[j].0, up[i].1 + up[j].1);
set.push((up[j], up2[j]));
}
set.sort(); set.dedup();
up[i] = f((up[i].0 % M, up[i].1 % M));
up2[i] = set.iter().map(|e| e.1).sum::<u64>()+1;
set.clear();
}
let mut down = vec![(0, 0); N];
let mut down2 = vec![0; N];
let mut ans = vec![];
let mut map = HashMap::new();
for &i in &order {
let mut sum = (0, 0);
let mut cnt = 1;
for &j in &E[i] {
if par[i] == j {
sum = (sum.0 + down[i].0, sum.1 + down[i].1);
let e = map.entry((down[i], down2[i])).or_insert(0);
if *e == 0 { cnt += down2[i]; }
*e += 1;
} else {
sum = (sum.0 + up[j].0, sum.1 + up[j].1);
let e = map.entry((up[j], up2[j])).or_insert(0);
if *e == 0 { cnt += up2[j]; }
*e += 1;
}
}
ans.push(((sum.0%M, sum.1%M), cnt));
for &j in &E[i] {
if par[i] == j { continue; }
let (sum, mut cnt) = ((sum.0-up[j].0, sum.1-up[j].1), cnt);
if map[&(up[j], up2[j])] == 1 {
cnt -= up2[j];
}
down[j] = f((sum.0 % M, sum.1 % M));
down2[j] = cnt;
}
map.clear();
}
ans.sort();
ans.dedup();
out << ans.iter().map(|e| e.1).sum::<u64>();
}
fn main() { out::init(INTERACTIVE || !SUBMISSION); solve(); out::print() }
mod external { pub use { proconio::{ input, input_interactive, marker::{Chars as chars, Usize1 as usize1} }, }; }
// my library: https://github.com/solalyth/cplib-rs
mod cplib { #![allow(unused_macros, dead_code)] pub const SUBMISSION: bool = true; pub mod prelude { pub use std::{ collections::{VecDeque, HashMap, HashSet, BTreeMap, BTreeSet, BinaryHeap}, cmp::{Ordering, Reverse}, mem::{swap, replace} }; pub use crate::cplib::{ *, SUBMISSION, util::{ output::{out, end} }, }; use std::fmt; #[derive(Clone, Copy, PartialEq, PartialOrd)] pub struct F64(pub f64); impl Eq for F64 {} impl Ord for F64 { fn cmp(&self, other: &Self) -> Ordering { self.partial_cmp(other).unwrap() } } impl fmt::Debug for F64 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } } pub mod debug { pub fn replace_inf(s: &str) -> String { let (mut res, mut stk) = (String::new(), String::new()); for c in s.chars().chain(std::iter::once('*')) { if c.is_numeric() || c == '.' { stk.push(c); continue; } if stk.parse::<f64>().map_or(false, |s| 1.15e18 <= s) { res += "inf"; } else { res += &stk; } stk.clear(); res.push(c); } res.pop(); res } #[macro_export] macro_rules! epr { ($($args:tt)*) => { if !$crate::SUBMISSION { eprintln!("\x1b[31m{}\x1b[0m", crate::debug::replace_inf(&format!($($args)*))); } } } #[macro_export] macro_rules! table { ($vv:expr) => { if !$crate::SUBMISSION { use std::fmt::Write; let (mut lmax, mut res) = (0, String::new()); let vv: Vec<Vec<String>> = $vv.iter().map(|v| v.iter().map(|e| { let t = crate::debug::replace_inf(&format!("{e:?}")); lmax = lmax.max(t.len()); t }).collect()).collect(); for v in &vv { res += " [ "; for e in v { write!(&mut res, "{e: >lmax$}, "); } res.pop(); res.pop(); res += "]\n"; } eprintln!("\x1b[38;5;208m{} = [\n{res}]\x1b[0m", stringify!($vv)); } }; } #[macro_export] macro_rules! oj_local { ($oj:expr, $local:expr) => { if $crate::SUBMISSION { $oj } else { $local } }; } } pub mod ds { } pub mod algo { } pub mod graph { } pub mod math { } pub mod mod998 { } pub mod util { pub mod output { #![allow(static_mut_refs, non_camel_case_types)] use std::{mem::replace, ops::{Not, Shl}, fmt::Write}; static mut BUFFER: Buffer = Buffer { buf: String::new(), endp: false, prev: Previous::LineHead }; pub struct Buffer { buf: String, endp: bool, prev: Previous, } impl Buffer { const LEN: usize = 16*1024*1024; fn print(&mut self) { if replace(&mut self.prev, Previous::LineHead) == Previous::LineHead { self.buf.pop(); } if crate::cplib::SUBMISSION { println!("{}", self.buf); } else { eprint!("\x1b[32m"); if self.buf.is_empty() { println!(">> (empty)"); } else { for s in self.buf.split('\n') { eprint!(">> "); println!("{s}"); } } eprint!("\x1b[0m"); } self.buf.clear(); } fn space(&mut self, sp: bool) { let prev = replace(&mut self.prev, if sp {Previous::Space} else {Previous::NoSpace}); if (sp || prev == Previous::Space) && prev != Previous::LineHead { self.buf.push(' '); } } } #[derive(Clone, Copy)] pub struct out; pub struct out_usp; pub struct end; #[derive(PartialEq, Eq, Clone, Copy)] enum Previous { Space, NoSpace, LineHead, } impl out { pub fn init(endp: bool) { unsafe { BUFFER.buf.reserve(Buffer::LEN); BUFFER.endp = endp; } } pub fn print() { unsafe { BUFFER.print(); } } pub fn space() { unsafe { if BUFFER.prev == Previous::NoSpace { BUFFER.prev = Previous::Space; } } } fn push<T: Primitive>(v: &T) { unsafe { BUFFER.space(true); v.fmt(&mut BUFFER.buf); } } } impl out_usp { fn push<T: Primitive>(v: &T) { unsafe { BUFFER.space(false); v.fmt(&mut BUFFER.buf); } } } impl Not for out { type Output = out_usp; fn not(self) -> Self::Output { out_usp } } macro_rules! impl_outs { ($($t:ty),+) => { $( impl<T: Primitive> Shl<T> for $t { type Output = Self; fn shl(self, rhs: T) -> Self::Output { Self::push(&rhs); self } } impl Shl<end> for $t { type Output = Self; fn shl(self, _: end) -> Self::Output { unsafe { if BUFFER.endp { BUFFER.print(); } else { BUFFER.buf += "\n"; BUFFER.prev = Previous::LineHead; } } self } } )+ }; } impl_outs!(out, out_usp); macro_rules! impl_for_slices { ($($t:ty),+) => { $(impl_for_slices!($t; out, out_usp);)+ }; ($t:ty; $($u:ty),+) => { $( impl<T: Primitive> Shl<$t> for $u { type Output = Self; fn shl(self, rhs: $t) -> Self::Output { for v in rhs { Self::push(v); } self } } )+} } impl_for_slices!(&[T], &Vec<T>); trait Primitive { fn fmt(&self, buf: &mut String); } macro_rules! impl_primitive { ($($t:ty),+) => { $( impl Primitive for $t { fn fmt(&self, buf: &mut String) { write!(buf, "{self}").ok(); } } )+ } } impl_primitive!(char, u32, u64, u128, usize, i32, i64, i128, f32, f64, &str, &String, String); impl Primitive for u8 { fn fmt(&self, buf: &mut String) { buf.push(*self as char); } } impl Primitive for bool { fn fmt(&self, buf: &mut String) { *buf += if *self { "Yes" } else { "No" }; } } } pub mod macros { #[macro_export] macro_rules! nest { [void; $n:expr] => { std::vec![std::vec![]; $n] }; [void; $n:expr $(;$m:expr)+] => { std::vec![crate::nest![void$(;$m)+]; $n] }; [$($v:expr),*] => { std::vec![$($v),*] }; [$e:expr; $n:expr] => { std::vec![$e; $n] }; [$e:expr; $n:expr $(;$m:expr)+] => { std::vec![crate::nest![$e$(;$m)+]; $n] }; } #[macro_export] macro_rules! min { ($($vl:expr),+) => { [$($vl),+].into_iter().reduce(|x,y| if x <= y {x} else {y}).unwrap() } } #[macro_export] macro_rules! max { ($($vl:expr),+) => { [$($vl),+].into_iter().reduce(|x,y| if x >= y {x} else {y}).unwrap() } } #[macro_export] macro_rules! chmin { ($dst:expr; $v:expr) => { { let v = $v; if v < $dst { $dst = v; true } else { false } } }; ($dst:expr; $($vl:expr),+) => { crate::chmin!($dst; crate::min!($($vl),+)) } } #[macro_export] macro_rules! chmax { ($dst:expr; $v:expr) => { { let v = $v; if $dst < v { $dst = v; true } else { false } } }; ($dst:expr; $($vl:expr),+) => { crate::chmax!($dst; crate::max!($($vl),+)) } } } } }
Solalyth