#![warn(clippy::pedantic)]

use std::{io::stdin, num::NonZeroU32};

#[derive(Debug, Clone, PartialEq, Eq)]
struct Card {
    winning: Vec<usize>,
    found: Vec<usize>,
}

impl Card {
    pub fn parse(s: &str) -> Self {
        fn get_numbers(s: &str) -> Vec<usize> {
            s.split(' ')
                .filter(|s| !s.is_empty())
                .map(|n| n.parse().unwrap())
                .collect()
        }

        let (_id, numbers) = s.split_once(": ").unwrap();
        let (winning, found) = numbers.split_once(" | ").unwrap();
        let winning = get_numbers(winning);
        let found = get_numbers(found);

        Self { winning, found }
    }

    pub fn num_matching(&self) -> usize {
        self.found
            .iter()
            .filter(|n| self.winning.contains(n))
            .count()
    }

    pub fn exp_score(&self) -> usize {
        if let Some(matching) = NonZeroU32::new(u32::try_from(self.num_matching()).unwrap()) {
            2_usize.pow(matching.get() - 1)
        } else {
            0
        }
    }

    pub fn recursive_score(&self, previous: &[usize]) -> usize {
        1 + previous
            .iter()
            .rev()
            .take(self.num_matching())
            .sum::<usize>()
    }
}

fn main() {
    let cards: Vec<Card> = stdin().lines().map(|l| Card::parse(&l.unwrap())).collect();

    println!("{}", cards.iter().map(Card::exp_score).sum::<usize>());

    let recursive_score: usize = cards
        .iter()
        .rev()
        .fold(Vec::new(), |mut previous, card| {
            let score = card.recursive_score(&previous);
            previous.push(score);
            previous
        })
        .iter()
        .sum();

    println!("{recursive_score}");
}
