結果
問題 | No.1322 Totient Bound |
ユーザー | akakimidori |
提出日時 | 2020-12-19 01:58:52 |
言語 | Rust (1.77.0 + proconio) |
結果 |
AC
|
実行時間 | 739 ms / 5,000 ms |
コード長 | 8,663 bytes |
コンパイル時間 | 13,065 ms |
コンパイル使用メモリ | 378,960 KB |
実行使用メモリ | 61,472 KB |
最終ジャッジ日時 | 2024-09-21 09:49:35 |
合計ジャッジ時間 | 25,604 ms |
ジャッジサーバーID (参考情報) |
judge3 / judge2 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 1 ms
5,248 KB |
testcase_01 | AC | 1 ms
5,376 KB |
testcase_02 | AC | 1 ms
5,376 KB |
testcase_03 | AC | 1 ms
5,376 KB |
testcase_04 | AC | 1 ms
5,376 KB |
testcase_05 | AC | 1 ms
5,376 KB |
testcase_06 | AC | 1 ms
5,376 KB |
testcase_07 | AC | 1 ms
5,376 KB |
testcase_08 | AC | 1 ms
5,376 KB |
testcase_09 | AC | 1 ms
5,376 KB |
testcase_10 | AC | 1 ms
5,376 KB |
testcase_11 | AC | 1 ms
5,376 KB |
testcase_12 | AC | 1 ms
5,376 KB |
testcase_13 | AC | 3 ms
5,376 KB |
testcase_14 | AC | 3 ms
5,376 KB |
testcase_15 | AC | 2 ms
5,376 KB |
testcase_16 | AC | 2 ms
5,376 KB |
testcase_17 | AC | 2 ms
5,376 KB |
testcase_18 | AC | 133 ms
15,384 KB |
testcase_19 | AC | 198 ms
21,112 KB |
testcase_20 | AC | 532 ms
45,932 KB |
testcase_21 | AC | 572 ms
50,192 KB |
testcase_22 | AC | 569 ms
50,928 KB |
testcase_23 | AC | 709 ms
61,208 KB |
testcase_24 | AC | 709 ms
61,400 KB |
testcase_25 | AC | 705 ms
61,468 KB |
testcase_26 | AC | 705 ms
61,340 KB |
testcase_27 | AC | 702 ms
61,336 KB |
testcase_28 | AC | 739 ms
61,472 KB |
testcase_29 | AC | 729 ms
61,336 KB |
testcase_30 | AC | 1 ms
5,376 KB |
testcase_31 | AC | 1 ms
5,376 KB |
testcase_32 | AC | 1 ms
5,376 KB |
testcase_33 | AC | 721 ms
61,472 KB |
testcase_34 | AC | 702 ms
60,896 KB |
testcase_35 | AC | 700 ms
61,340 KB |
testcase_36 | AC | 697 ms
61,160 KB |
testcase_37 | AC | 688 ms
60,880 KB |
testcase_38 | AC | 712 ms
61,192 KB |
ソースコード
// 昔の提出から // https://atcoder.jp/contests/xmascon19/submissions/11229505 struct FenwickTree { val: Vec<u64>, } impl FenwickTree { fn new(n: usize) -> Self { FenwickTree { val: vec![0; n + 1], } } fn add(&mut self, mut x: usize, v: u64) { while let Some(p) = self.val.get_mut(x) { *p = p.wrapping_add(v); x += x & (!x + 1); } } fn sum(&mut self, mut x: usize) -> u64 { assert!(x < self.val.len()); let mut ans = 0u64; while x > 0 { ans = ans.wrapping_add(self.val[x]); x -= x & (!x + 1); } ans } } fn sieve(n: usize) -> Vec<u64> { let mut is_prime = vec![true; n + 1]; for i in 2.. { if i * i > n { break; } if is_prime[i] { let mut j = i * i; while j <= n { is_prime[j] = false; j += i; } } } let len = is_prime.iter().skip(2).filter(|p| **p).count(); let mut prime = Vec::with_capacity(len); for (i, is_prime) in is_prime.into_iter().enumerate().skip(2) { if is_prime { prime.push(i as u64); } } prime } struct PrimeCountSolver { query: Vec<u64>, memo: Vec<u64>, } impl PrimeCountSolver { fn new() -> Self { PrimeCountSolver { query: vec![], memo: vec![], } } fn add(&mut self, n: u64) { self.query.push(n); } fn build(&mut self) { self.query.sort(); self.query.dedup(); let n = self.query.last().map_or(1, |n| *n); let mut m = 1; while (m + 1) * (m + 1) <= n { m += 1; } let p = sieve(m as usize); let bound = (n as f64).cbrt().powi(2) as u64 + 2; let mut stack = vec![]; for &v in self.query.iter() { let k = match p.binary_search_by(|&p| (p * p).cmp(&v)) { Ok(k) => k + 1, Err(k) => k, }; stack.push((v, k)); } let mut query = vec![]; while let Some((n, k)) = stack.pop() { if k == 0 { continue; } let q = p[k - 1]; if q * q > n { let x = match p[..k].binary_search_by(|&p| (p * p).cmp(&n)) { Ok(k) => k + 1, Err(k) => k, }; stack.push((n, x)); } else if n <= bound { query.push((k, n)); } else { stack.push((n, k - 1)); stack.push((n / q, k - 1)); } } query.sort(); query.dedup(); let m = bound as usize; let mut bit = FenwickTree::new(m); for i in 1..(m + 1) { bit.add(i, 1); } let mut is_prime = vec![true; m + 1]; let mut memo = vec![0; query.len()]; let mut pos = 0; for (i, p) in p.iter().enumerate() { let p = *p as usize; let mut j = p; while j <= m { if is_prime[j] { bit.add(j, !0); } is_prime[j] = false; j += p; } while let Some(&(k, n)) = query.get(pos) { if k > i + 1 { break; } else { memo[pos] = bit.sum(n as usize); pos += 1; } } if pos >= query.len() { break; } } self.memo.clear(); self.memo.resize(self.query.len(), 0); let mut stack = vec![]; for (i, &n) in self.query.iter().enumerate() { let k = match p.binary_search_by(|&p| (p * p).cmp(&n)) { Ok(k) => k + 1, Err(k) => k, }; self.memo[i] += k as u64 - 1; stack.push((n, k, 1, i)); } while let Some((n, k, sign, i)) = stack.pop() { if k == 0 { self.memo[i] += sign * n; continue; } let q = p[k - 1]; if q * q > n { let x = match p[..k].binary_search_by(|p| (p * p).cmp(&n)) { Ok(k) => k + 1, Err(k) => k, }; self.memo[i] += (1 ^ !sign) * (k - x) as u64; stack.push((n, x, sign, i)); } else if n <= bound { self.memo[i] += sign * memo[query.binary_search(&(k, n)).unwrap()]; } else { stack.push((n, k - 1, sign, i)); stack.push((n / q, k - 1, 1 ^ !sign, i)); } } } fn get(&self, n: u64) -> u64 { self.memo[self.query.binary_search(&n).unwrap()] } } // 昔の提出終わり // ---------- begin enumerate prime ---------- fn enumerate_prime(n: usize) -> Vec<usize> { assert!(n <= 10usize.pow(8)); if n <= 1 { return vec![]; } let batch = (n as f64).sqrt() as usize + 1; let mut is_prime = vec![true; batch + 1]; for i in (2..).take_while(|p| p * p <= batch) { if is_prime[i] { let mut j = i * i; while let Some(p) = is_prime.get_mut(j) { *p = false; j += i; } } } let mut prime = vec![]; let mut small_prime = vec![]; for (i, p) in is_prime.iter().enumerate().skip(2) { if *p && i <= n { prime.push(i); small_prime.push(i); } } let mut l = batch; while l <= n { let r = std::cmp::min(l + batch, n + 1); is_prime.clear(); is_prime.resize(r - l, true); for &p in small_prime.iter() { let mut j = (l + p - 1) / p * p - l; while let Some(is_prime) = is_prime.get_mut(j) { *is_prime = false; j += p; } } for (i, p) in is_prime.iter().enumerate().filter(|p| *p.1) { if *p { prime.push(i + l); } } l += batch; } prime } // ---------- end enumerate prime ---------- // ? // 知ってること // N*2*3/2*5/4*... でテキトーな上限Mが計算できて、 // phi(x) <= N, x <= M なものを数えられればいい // x = ** p^a // phi(x) = ** (p-1)*p^(a-1) // phi(x) <= x // 素数カウント、phiの和 // // 最後にかける素数についてのみなら素数カウントが使えないこともない // それまでの列挙の計算量が爆発してるから厳しいのでは // よく考えれば考える状態自体はsqrt(N) ではある // でもsqrt(N) くらい遷移する必要があるからやっぱ厳しいのでは // もっと早い段階で枝刈りができるんじゃないか? // できそう // (p-1)^2 > K なやつはさっさと素数カウントの方に突っ込んでいくと良さげ #[allow(dead_code)] fn run(n: usize) { let m = (1..).find(|m| (m - 1) * (m - 1) > n).unwrap() - 1; let prime = enumerate_prime(m); let mut dp = std::collections::BTreeMap::new(); let mut count = std::collections::BTreeMap::new(); let mut ans = 0; dp.insert(n, 1); for (i, &p) in prime.iter().enumerate() { let mut memo = vec![]; for (&n, &c) in dp.iter().rev().take_while(|e| *e.0 >= p - 1) { let mut d = p - 1; while d <= n { memo.push((n / d, c)); d *= p; } } for (n, v) in memo { *dp.entry(n).or_insert(0) += v; } let mut del = vec![]; for (&n, &c) in dp.iter() { if (p - 1).pow(2) <= n { break; } del.push((n, c)); } for (n, c) in del { dp.remove(&n); *count.entry(n + 1).or_insert(0) += c; let x = prime.binary_search_by_key(&(n + 1, 1), |p| (*p, 0)).unwrap_err(); ans -= x.min(i + 1) * c; } } for (n, c) in dp { *count.entry(n + 1).or_insert(0) += c; ans -= prime.len() * c; } let mut solver = PrimeCountSolver::new(); for (&n, _) in count.iter() { solver.add(n as u64); } solver.build(); for (&n, &c) in count.iter() { ans += (solver.get(n as u64) as usize + 1) * c; } println!("{}", ans); } fn main() { let mut s = String::new(); std::io::stdin().read_line(&mut s).unwrap(); let n: usize = s.trim().parse().unwrap(); run(n); }