Processing math: 100%
We will find a way, we always have.

-interstellar

Problem Solving/λ°±μ€€

[λ°±μ€€] 16877번: ν•Œλ²„

Redddy 2025. 3. 30. 18:51

πŸ”ˆ 문제

koosaga와 cubeloverκ°€ "ν•Œλ²„"λ₯Ό ν•˜κ³  μžˆλ‹€. ν•Œλ²„λŠ” λ‹˜ κ²Œμž„μ— κ·œμΉ™μ„ μΆ”κ°€ν•œ κ²Œμž„μ΄λ‹€. ν•Œλ²„λŠ” λŒμ„ 차곑 차곑 μœ„λ‘œ μŒ“μ•„μ˜¬λ¦° 돌 더미 k개λ₯Ό μ΄μš©ν•œλ‹€. 각각의 돌 λ”λ―Έμ—λŠ” ν•œ 개 μ΄μƒμ˜ 돌이 μžˆλ‹€. 두 μ‚¬λžŒμ€ μ„œλ‘œ 턴을 λ²ˆκ°ˆμ•„κ°€λ©΄μ„œ ν•Œλ²„λ₯Ό μ§„ν–‰ν•œλ‹€. 각 μ‚¬λžŒμ˜ 턴이 되면, 돌 더미 ν•˜λ‚˜λ₯Ό 선택해 λŒμ„ μ œκ±°ν•œλ‹€. μ œκ±°ν•œ 돌의 κ°œμˆ˜λŠ” ν”Όλ³΄λ‚˜μΉ˜ μˆ˜μ—¬μ•Ό ν•œλ‹€. 전체 돌 λ”λ―Έμ—μ„œ λ§ˆμ§€λ§‰ λŒμ„ μ œκ±°ν•˜λŠ” μ‚¬λžŒμ΄ κ²Œμž„μ„ 이기게 λœλ‹€.  κ²Œμž„μ€ koosagaκ°€ λ¨Όμ € μ‹œμž‘ν•œλ‹€. 두 μ‚¬λžŒμ΄ 졜적의 λ°©λ²•μœΌλ‘œ κ²Œμž„μ„ μ§„ν–‰ν–ˆμ„ λ•Œ, μ΄κΈ°λŠ” μ‚¬λžŒμ„ 좜λ ₯ν•œλ‹€.

 

πŸ“μž…λ ₯

첫째 쀄에 돌 λ”λ―Έμ˜ 개수 N (1≀N≀105)이 주어진닀. λ‘˜μ§Έ 쀄에 각 돌 더미에 μŒ“μ—¬μžˆλŠ” 돌의 개수 Pi (1≀Pi≀3Γ—106)κ°€ 주어진닀.

 

 

πŸ“‘μΆœλ ₯

koosagaκ°€ μ΄κΈ°λŠ” κ²½μš°μ—λŠ” "koosaga"λ₯Ό, cubeloverκ°€ μ΄κΈ°λŠ” κ²½μš°μ—λŠ” "cubelover"λ₯Ό 좜λ ₯ν•œλ‹€.

 

πŸ“š λ¬Έμ œ ν’€μ΄

 

μŠ€ν”„λΌκ·Έ κ·ΈλŸ°λ”” λ¬Έμ œμ˜€λ‹€. (λ‚˜μ€‘μ— μ‹œκ°„μ΄ λœλ‹€λ©΄ μŠ€ν”„λΌκ·Έ κ·ΈλŸ°λ”” 글도 μ¨λ³΄λŠ” 걸둜,,,)

 

μ†μœΌλ‘œ μ“±μ‹Ή 20κΉŒμ§€μ˜ κ·ΈλŸ°λ”” λ„˜λ²„λ₯Ό κ³„μ‚°ν–ˆμ„ λ•Œ, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5 이 λ°˜λ³΅λ˜λŠ” κ·œμΉ™μ΄ 보여 μ£ΌκΈ°κ°€ 10인 κ·ΈλŸ°λ”” λ„˜λ²„μΈμ€„ μ•Œμ•˜λ‹€. ν•˜μ§€λ§Œ 이 κ·œμΉ™μ€ 40 이상뢀터 κΉ¨μ‘Œλ‹€. πŸ˜…

 

κ²°κ΅­ P의 μ΅œλŒ€κ°’μΈ 3βˆ—106 κΉŒμ§€μ˜ λͺ¨λ“  κ·ΈλŸ°λ”” λ„˜λ²„λ₯Ό μ°Ύκ³  xor 합을 μ·¨ν•˜λŠ” λ¬Έμ œμ˜€λ‹€. 이게 이 문제의 핡심인데, κ·ΈλŸ°λ”” λ„˜λ²„λ₯Ό μ°ΎκΈ° μœ„ν•΄μ„œ mex ν•¨μˆ˜λ₯Ό 돌릴 λ•Œ 이것저것 ν•΄λ³΄λ©΄μ„œ μ‹€ν–‰ 속도λ₯Ό 쀄이기 μœ„ν•œ 과정을 λ‚¨κ²¨λ³΄κ³ μž ν•œλ‹€.

 

 

244ms

 

처음으둜 μ •λ‹΅ νŒμ •μ„ 받은 μ½”λ“œμ΄λ‹€.

 

const FIBO: [usize; 32] = [
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,
17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578,
];
fn mex(mut arr: Vec<bool>) -> u32 {
for i in 0..arr.len() {
if !arr[i] {
return i as u32;
}
}
arr.len() as u32
}
fn get_grundy() -> Vec<u32> {
let n = 3_000_000;
let mut grundy = vec![0u32; n + 1];
grundy[1] = 1;
for i in 2..=n {
let mut visited: Vec<bool> = vec![false; 16];
for j in FIBO {
if i >= j {
visited[grundy[i - j] as usize] = true;
} else {
break;
}
}
grundy[i] = mex(visited);
}
grundy
}
view raw fimbor1.rs hosted with ❀ by GitHub

 

μ£Όμš” λΆ€λΆ„λ§Œ μ‚΄νŽ΄λ³΄μžλ©΄, μš°μ„ μ€ ν”„λ‘œκ·Έλž¨μœΌλ‘œ λŒλ €λ³΄μ•˜μ„ λ•Œ κ·ΈλŸ°λ”” μˆ«μžκ°€ μ΅œλŒ€ 15κΉŒμ§€λΌλŠ” 것을 νŒŒμ•…ν–ˆλ‹€. ν‰μ†Œμ—λŠ” 쀑볡 제거λ₯Ό μœ„ν•΄ κ·ΈλŸ°λ”” λ„˜λ²„λ₯Ό μ €μž₯ν•  λ•Œ HashSet을 μ‚¬μš©ν–ˆμ—ˆλŠ”λ°, μ—¬κΈ°μ„œλŠ” 길이16짜리 bool 배열을 λ§Œλ“€μ–΄μ„œ ν™•μΈν•˜λŠ” 것이 더 효율적이라고 νŒλ‹¨μ„ ν–ˆλ‹€.

 

 

216 ms

첫 μ •λ‹΅μ½”λ“œλ³΄λ‹€ 30ms κ°€ μ€„μ—ˆλŠ”λ°, 두 μ½”λ“œμ˜ 차이점은 μ „μ—­ λ³€μˆ˜λ‘œ μ„ μ–Έλœ FIBOλ₯Ό 둜컬 λ³€μˆ˜λ‘œ λ³€κ²½ν•œ 것이닀. κ·Έλž¬λ”λ‹ˆ μž‘μ€ 것 κ°™μœΌλ©΄μ„œλ„ 큰 30msκ°€ μ€„μ—ˆλ‹€.

 

fn mex(mut arr: Vec<bool>) -> u32 {
for i in 0..arr.len() {
if !arr[i] {
return i as u32;
}
}
arr.len() as u32
}
fn get_grundy() -> Vec<u32> {
let n = 3_000_000;
let mut grundy = vec![0u32; n + 1];
grundy[1] = 1;
let fibo = vec![
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,
17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578];
for i in 2..=n {
let mut visited: Vec<bool> = vec![false; 16];
for j in &fibo {
if i >= *j {
visited[grundy[i - *j] as usize] = true;
} else {
break;
}
}
grundy[i] = mex(visited);
}
grundy
}
view raw fimbor2.rs hosted with ❀ by GitHub

 

 

112 ms

 

거의 두배 κ°€κΉŒμ΄ μ€„μ˜€λ‹€. μ΄λ•ŒλŠ” 배열에 μ €μž₯ν•˜λŠ” 방법이 μ•„λ‹Œ, λΉ„νŠΈλ§ˆμŠ€ν‚Ήμ„ μ‚¬μš©ν•΄ mex ν•¨μˆ˜λ₯Ό λŒ€μ²΄ν–ˆλ‹€. μ•žμ„œ λ§ν–ˆλ‹€μ‹œν”Ό κ·ΈλŸ°λ”” λ„˜λ²„κ°€ μ΅œλŒ€ 15λ₯Ό λ„˜μ§€ μ•ŠλŠ”λ‹€. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— ꡳ이 16짜리의 bool 배열을 μ„ μ–Έν•˜λŠ” 것이 μ•„λ‹Œ 16λΉ„νŠΈμ˜ 숫자 ν•˜λ‚˜λ‘œλ„ λͺ¨λ“  것을 ν‘œν˜„ν•  수 μžˆλ‹€! (ꡉμž₯히 μ“°κ»„ν•˜μ§€ μ•„λ‹ˆν•œκ°€)

 

λΉ„νŠΈλ§ˆμŠ€ν‚Ήμ„ κ°„λ‹¨ν•˜κ²Œ μ„€λͺ…ν•΄λ³΄μžλ©΄, bool 둜 μ„ μ–Έν–ˆμ„ λ•ŒλŠ” 예λ₯Ό λ“€μ–΄ visited[3] κ°€ true 인지 false인지 νŒλ‹¨μ„ ν•΄μ„œ ν•΄λ‹Ή 값이 μ‘΄μž¬ν–ˆλŠ”μ§€ νŒŒμ•…ν–ˆλ‹€. 그런데 λΉ„νŠΈλ§ˆμŠ€ν‚Ήμ„ μ‚¬μš©ν•œλ‹€λ©΄ 배열에 인덱슀둜 μ ‘κ·Όν•˜λŠ” 것이 μ•„λ‹ˆλΌ μˆ«μžμ— νŠΉμ • λΉ„νŠΈκ°€ 0인지 1인지 νŒλ‹¨ ν•˜λŠ” 것이닀.

 

fn get_grundy() -> Vec<u32> {
let n = 3_000_000;
let mut grundy = vec![0u32; n + 1];
grundy[1] = 1;
let fibo = vec![
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,
17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578];
for i in 2..=n {
let mut bit = 0i32;
for j in &fibo {
if i >= *j {
bit |= 1 << grundy[i - *j];
} else {
break;
}
}
grundy[i] = bit.trailing_ones();
}
grundy
}
view raw fimbor3.rs hosted with ❀ by GitHub

 

μ΄λ•ŒλŠ” λ”°λ‘œ mex ν•¨μˆ˜λ₯Ό λ§Œλ“€μ§€ μ•Šκ³ , 러슀트의 trailing_ones ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ˜€λ‹€. trailing_ones ν•¨μˆ˜λŠ” κ°€μž₯ 였λ₯Έμͺ½(least significant bit, LSB)λΆ€ν„° μ—°μ†λœ 1의 개수λ₯Ό λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λ‹€.

 

 

68ms

 

λΉ„νŠΈλ§ˆμŠ€ν‚Ή μ μš©μ‹œν‚¨ 것에 λ©ˆμΆ”μ§€ μ•Šκ³  ν•œ 발 더 λ‚˜μ•„κ°€ λ‚­λΉ„λ˜κ³  μžˆλŠ” λΉ„νŠΈλ₯Ό μ ˆμ•½ν•˜μ˜€λ‹€.

 

μ—¬λŸ¬ μ°¨λ‘€ μ–ΈκΈ‰ν•˜κ³  μžˆλŠ”λ° κ·ΈλŸ°λ”” μˆ˜λŠ” μ΅œλŒ€ 15이닀. κ·Έλž˜μ„œ ꡳ이 u32 (32λΉ„νŠΈ 음이 μ•„λ‹Œ μ •μˆ˜) λ₯Ό μ‚¬μš©ν•  ν•„μš”κ°€ μ—†κ³  u8(8λΉ„νŠΈ 음이 μ•„λ‹Œ μ •μˆ˜)λ₯Ό μ‚¬μš©ν•΄λ„ μΆ©λΆ„ν–ˆλ‹€. κ·Έλž˜μ„œ μ‚¬μš©ν•˜λ˜ μžλ£Œν˜•μ„ λ³€κ²½ν•΄μ£Όμ—ˆλ”λ‹ˆ μ‹œκ°„μ΄ 50ms 정도 κ°μ†Œν•˜μ˜€λ‹€.

 

fn get_grundy() -> Vec<u8> {
let n = 3_000_000;
let mut grundy = vec![0u8; n + 1]; // grundy 배열을 u32 μ—μ„œ u8둜.
grundy[1] = 1;
let fibo = vec![
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,
17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578];
for i in 2..=n {
let mut bit = 0i16; // μ €μž₯ν•  λΉ„νŠΈλ₯Ό u32 μ—μ„œ u16으둜
for j in &fibo {
if i >= *j {
bit |= 1 << grundy[i - *j];
} else {
break;
}
}
grundy[i] = bit.trailing_ones() as u8;
}
grundy
}
view raw fimbor4.rs hosted with ❀ by GitHub

 

 

 

πŸ’» μ΅œμ’… μ½”λ“œ

use std::io::{read_to_string, stdin};
fn main() {
let stdin = read_to_string(stdin()).unwrap();
let mut token = stdin.split_whitespace();
let mut next = || token.next().unwrap();
let n: usize = next().parse().unwrap();
let nums: Vec<u32> = (0..n).map(|_| next().parse().unwrap()).collect();
print!("{}", solve(n, nums));
}
fn solve(_n: usize, nums: Vec<u32>) -> String {
let mut res =0 ;
let nim = get_grundy();
for num in nums {
res ^= nim[num as usize];
}
if res != 0 {
"koosaga".to_string()
} else {
"cubelover".to_string()
}
}
fn get_grundy() -> Vec<u8> {
let n = 3_000_000;
let mut grundy = vec![0u8; n + 1];
grundy[1] = 1;
let fibo = vec![
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,
17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578];
for i in 2..=n {
let mut bit = 0i16;
for j in &fibo {
if i >= *j {
bit |= 1 << grundy[i - *j];
} else {
break;
}
}
grundy[i] = bit.trailing_ones() as u8;
}
grundy
}
view raw fimbor5.rs hosted with ❀ by GitHub

 

 

🍭 μ‹œν–‰μ°©μ˜€

 

 

맞힌 μ‚¬λžŒ μ „μ²΄μ—μ„œλ„ 일등을 λ¨Ήκ³  μžˆλ‹€.😎 (2025.3.30 κΈ°μ€€)

 

 

 

πŸ”— 문제링크 ν•Œλ²„