MojoでProject Euler 55

https://projecteuler.net/problem=55

そのままですね。

import sys


#################### library ####################

fn digits(owned n: Int) -> List[Int]:
    var ds = List[Int]()
    while n > 0:
        var d = n % 10
        ds.append(d)
        n //= 10
    return ds

fn reverse_number(n: Int) -> Int:
    var ds = digits(n)
    var m = 0
    for d in ds:
        m = m * 10 + d[]
    return m


#################### process ####################

fn is_palindromic(n: Int) -> Bool:
    var m = reverse_number(n)
    return m == n

fn next_number(n: Int) -> Int:
    return n + reverse_number(n)

fn is_Lychrel(owned n: Int) -> Bool:
    for _ in range(50):
        n = next_number(n)
        if is_palindromic(n):
            return False
    else:
        return True

fn f(N: Int) -> Int:
    var counter = 0
    for n in range(1, N):
        if is_Lychrel(n):
            counter += 1
    return counter

fn main() raises:
    var args = sys.argv()
    var N = atol(args[1])
    print(f(N))

AtCoder Beginner Contest 366 E

https://atcoder.jp/contests/abc366/tasks/abc366_e

これも累積和を使います。
x方向とy方向は分解できるので、それぞれで計算して、あとで統合します。
 \displaystyle A_x = \sum_{x'=x_{min}}^x{n_{x'}x'}
 \displaystyle B_x = \sum_{x'=x_{min}}^x{n_{x'}}
 n_xはxとなる座標の個数
とすると、
xでのx方向の距離の和は、
 x \lt x_{min}なら、
 \sum_{x'}{n_{x'}(x'-x)} = A_{x_{max}} - xB_{x_{max}}
 x \gt x_{max}なら、
 \sum_{x'}{n_{x'}(x-x')} = -A_{x_{max}} + xB{x_{max}}
 x_{min} \le x \le x_{max}なら、
 \sum_{x'}{n_{x'}|x-x'|} = \sum_{x'=x_{min}}^x{n_{x'}(x-x')} + \sum_{x'=x+1}^{x_{max}}{n_{x'}(x'-x)} = 2xB_x - xB_{x_{max}} - 2A_x + A_{x_{max}}
となります。
x方向とy方向のそれぞれの距離の和が求められれば、あとは尺取り法的にDを超えない距離の和を求めればよいです。

// Manhattan Multifocal Ellipse
#![allow(non_snake_case)]


//////////////////// library ////////////////////

fn read<T: std::str::FromStr>() -> T {
    let mut line = String::new();
    std::io::stdin().read_line(&mut line).ok();
    line.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    read::<String>().split_whitespace()
            .map(|e| e.parse().ok().unwrap()).collect()
}


//////////////////// process ////////////////////

fn read_input() -> (usize, Vec<(i64, i64)>) {
    let v: Vec<usize> = read_vec();
    let (N, D) = (v[0], v[1]);
    let points: Vec<(i64, i64)> = (0..N).map(|_| read_vec::<i64>()).
                                            map(|v| (v[0], v[1])).collect();
    (D, points)
}

// xもyも最小が1にする
fn convert_points(points: Vec<(i64, i64)>) -> Vec<(usize, usize)> {
    let min_x = points.iter().map(|&(x, _)| x).min().unwrap();
    let min_y = points.iter().map(|&(_, y)| y).min().unwrap();
    points.into_iter().map(|(x, y)| ((x - min_x + 1) as usize,
                                     (y - min_y + 1) as usize)).
                                                collect::<Vec<(usize, usize)>>()
}

fn accumulate(xs: Vec<usize>) -> (Vec<usize>, Vec<usize>) {
    let min_x = *xs.iter().next().unwrap();
    let max_x = *xs.iter().last().unwrap();
    let mut ns: Vec<usize> = vec![0; max_x - min_x + 1];
    for x in xs.into_iter() {
        ns[x-min_x] += 1
    }
    let mut a: Vec<usize> = vec![0; max_x - min_x + 2];
    let mut c: Vec<usize> = vec![0; max_x - min_x + 2];
    for (i, n) in ns.into_iter().enumerate() {
        a[i+1] += a[i] + n * (min_x + i);
        c[i+1] += c[i] + n
    }
    (a, c)
}

fn sum_dist(mut xs: Vec<usize>) -> (Vec<usize>, Vec<usize>) {
    xs.sort();
    let (a, c) = accumulate(xs);
    let L = a.len();
    let last_a = a[L-1];
    let last_c = c[L-1];
    let mut dists = vec![0; L];
    for i in 0..a.len() {
        dists[i] = i * 2 * c[i] + last_a - i * last_c - 2 * a[i];
    }
    
    // 個数が半分のところで分割して両方とも昇順になるようにする
    let mid = (0..L).filter(|&i| c[i] * 2 >= last_c).next().unwrap();
    (dists[..mid].into_iter().rev().map(|&x| x).collect::<Vec<usize>>(),
        dists[mid..].to_vec())
}

fn count_points(ds1: &Vec<usize>, ds2: &Vec<usize>,
                                N: usize, D: usize) -> usize {
    let dist = |l, ds: &Vec<usize>| {
        if l < ds.len() {
            ds[l]
        }
        else {
            ds[ds.len()-1] + (l - ds.len() + 1) * N
        }
    };
    
    let mut counter: usize = 0;
    if ds1[0] + ds2[0] > D {
        return 0
    }
    
    let mut l = 0;
    while dist(0, ds1) + dist(l, ds2) <= D {
        l += 1
    }
    l -= 1;
    counter += l + 1;
    for k in 1.. {
        if dist(k, ds1) + ds2[0] > D {
            break
        }
        else if l == 0 {
            counter += 1;
            continue
        }
        while dist(k, ds1) + dist(l, ds2) > D {
            l -= 1
        }
        counter += l + 1
    }
    counter
}

fn F(D: usize, points_: Vec<(i64, i64)>) -> usize {
    let N = points_.len();
    let points = convert_points(points_);
    let xs: Vec<usize> = points.iter().map(|&(x, _)| x).collect();
    let ys: Vec<usize> = points.iter().map(|&(_, y)| y).collect();
    let (dist_xs1, dist_xs2) = sum_dist(xs);
    let (dist_ys1, dist_ys2) = sum_dist(ys);
    count_points(&dist_xs1, &dist_ys1, N, D) +
    count_points(&dist_xs1, &dist_ys2, N, D) +
    count_points(&dist_xs2, &dist_ys1, N, D) +
    count_points(&dist_xs2, &dist_ys2, N, D)
}

fn main() {
    let (D, points) = read_input();
    println!("{}", F(D, points))
}

AtCoder Beginner Contest 366 D

https://atcoder.jp/contests/abc366/tasks/abc366_d

 \displaystyle P_{x,y,z} = \sum_{x_1=1}^x{\sum_{y_1=1}^y{\sum_{z_1=1}^z{A_{x_1,y_1,z_1}}}}
を計算しておけば包除原理で求められますが、このPを求めるときも包除原理を使います。

// Cuboid Sum Query
#![allow(non_snake_case)]


//////////////////// library ////////////////////

fn read<T: std::str::FromStr>() -> T {
    let mut line = String::new();
    std::io::stdin().read_line(&mut line).ok();
    line.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    read::<String>().split_whitespace()
            .map(|e| e.parse().ok().unwrap()).collect()
}


//////////////////// process ////////////////////

fn read_input() -> (Vec<Vec<Vec<i32>>>, usize) {
    let N: usize = read();
    let A: Vec<Vec<Vec<i32>>> = (0..N).map(|_|
                                (0..N).map(|_| read_vec::<i32>()).
                                                collect::<Vec<Vec<i32>>>()).
                                                collect();
    let Q: usize = read();
    (A, Q)
}

fn accumulate(A: Vec<Vec<Vec<i32>>>) -> Vec<Vec<Vec<i32>>> {
    let N = A.len();
    let mut P: Vec<Vec<Vec<i32>>> = vec![vec![vec![0; N+1]; N+1]; N+1];
    P[0][0][0] = A[0][0][0];
    for x in 0..N {
        for y in 0..N {
            for z in 0..N {
                P[x+1][y+1][z+1] = A[x][y][z]
                                    + P[x][y+1][z+1]
                                    + P[x+1][y][z+1]
                                    + P[x+1][y+1][z]
                                    - P[x][y][z+1]
                                    - P[x][y+1][z]
                                    - P[x+1][y][z]
                                    + P[x][y][z];
            }
        }
    }
    P
}

fn F_each(v: Vec<usize>, P: &Vec<Vec<Vec<i32>>>) -> i32 {
    let (Lx, Rx, Ly, Ry, Lz, Rz) = (v[0], v[1], v[2], v[3], v[4], v[5]);
    P[Rx][Ry][Rz] - P[Lx-1][Ry][Rz] - P[Rx][Ly-1][Rz] - P[Rx][Ry][Lz-1] +
                    P[Lx-1][Ly-1][Rz] + P[Lx-1][Ry][Lz-1] + P[Rx][Ly-1][Lz-1] -
                    P[Lx-1][Ly-1][Lz-1]
}

fn F(A: Vec<Vec<Vec<i32>>>, Q: usize) {
    let P = accumulate(A);
    for _ in 0..Q {
        let LR: Vec<usize> = read_vec();
        println!("{}", F_each(LR, &P))
    }
}

fn main() {
    let (A, Q) = read_input();
    F(A, Q)
}

MojoでProject Euler 54

https://projecteuler.net/problem=54

各役をC++のように基底クラスを継承したいのですが、どうやったらいいのでしょうね。同じくTraitを使うRustは頑張れば同じVecに入れられるのですが、MojoだとListに入れられるのでしょうか。

import sys


#################### List ####################

trait Printable(CollectionElement, Stringable):
    pass

fn print_list[T: Printable](a: List[T]):
    if a.size > 0:
        var s = "[" + str(a[0])
        for i in range(1, a.size):
            s += ", " + str(a[i])
        s += "]"
        print(s)
    else:
        print("[]")

fn reverse_list[T: CollectionElement](a: List[T]) -> List[T]:
    var b = List[T]()
    for i in range(len(a) - 1, -1, -1):
        b.append(a[i])
    return b


#################### Card ####################

@value
struct Card:
    var suit: String
    var number: Int
    
    @staticmethod
    fn create(s: String) raises -> Card:
        var suit = s[1]
        if s[0] == 'T':
            return Card(suit, 10)
        elif s[0] == 'J':
            return Card(suit, 11)
        elif s[0] == 'Q':
            return Card(suit, 12)
        elif s[0] == 'K':
            return Card(suit, 13)
        elif s[0] == 'A':
            return Card(suit, 14)
        else:
            var num = atol(s[0])
            return Card(suit, num)


#################### Rank ####################

@value
struct Rank:
    var freq: List[Int]
    var rank: Int
    
    fn __init__(inout self, freq: List[Int], name: String):
        self.freq = freq
        if name == "HighCard":
            self.rank = 1
        elif name == "OnePair":
            self.rank = 2
        elif name == "TwoPairs":
            self.rank = 3
        elif name == "ThreeCard":
            self.rank = 4
        elif name == "Straight":
            self.rank = 5
        elif name == "Flush":
            self.rank = 6
        elif name == "FullHouse":
            self.rank = 7
        elif name == "FourCards":
            self.rank = 8
        elif name == "StraightFlush":
            self.rank = 9
        else:
            self.rank = 10
    
    fn sum_cards(self) -> Int:
        var s = 0
        for elem in self.freq:
            var num = elem[] & 15
            var f = elem[] >> 4
            s += num * f
        return s
    
    fn __gt__(self, other: Rank) -> Bool:
        if self.rank != other.rank:
            return self.rank > other.rank
        
        for i in range(len(self.freq)):
            if self.freq[i] != other.freq[i]:
                return self.freq[i] > other.freq[i]
        else:
            return False
    
    @staticmethod
    fn create(hand: Hand) -> Rank:
        var freq = hand.frequency()
        if hand.is_straight():
            if hand.is_flush():
                if hand.sum() == 60:
                    return Rank(freq, "RolyalFlush")
                else:
                    return Rank(freq, "StraightFlush")
            else:
                return Rank(freq, "Straight")
        elif hand.is_flush():
            return Rank(freq, "Flush")
        elif len(freq) == 5:
            return Rank(freq, "HighCard")
        elif len(freq) == 4:
            return Rank(freq, "OnePair")
        elif len(freq) == 3:
            if (freq[0] & 15) == 2:
                return Rank(freq, "TwoPairs")
            else:
                return Rank(freq, "ThreeCard")
        else:
            if (freq[0] & 15) == 3:
                return Rank(freq, "FullHouse")
            else:
                return Rank(freq, "FourCards")


#################### Hand ####################

@value
struct Hand:
    var cards: List[Card]
    
    fn frequency(self) -> List[Int]:
        var s = 0
        for card in self.cards:
            s += 1 << (3 * card[].number)
        
        # [num + (freq << 4)]
        var freq = List[Int]()
        for num in range(2, 15):
            var f = (s >> (num * 3)) & 7
            if f != 0:
                freq.append(num + (f << 4))
        sort(freq)
        return reverse_list(freq)
    
    fn is_flush(self) -> Bool:
        for card in self.cards:
            if card[].suit != self.cards[0].suit:
                return False
        else:
            return True
    
    fn is_straight(self) -> Bool:
        var s = 0
        for card in self.cards:
            s |= 1 << card[].number
        while (s & 1) == 0:
            s >>= 1
        return s == 31
    
    fn sum(self) -> Int:
        var s = 0
        for card in self.cards:
            s += card[].number
        return s
    
    fn compare_straight(self, other: Hand) -> Bool:
        var bf1 = self.is_flush()
        var bf2 = other.is_flush()
        if bf1 == bf2:
            return self.sum() > other.sum()
        else:
            return bf1
    
    fn __gt__(self, other: Hand) -> Bool:
        var rank1 = Rank.create(self)
        var rank2 = Rank.create(other)
        return rank1 > rank2
    
    @staticmethod
    fn read(path: String) -> List[Hand]:
        try:
            var hands = List[Hand]()
            with open(path, "r") as f:
                var data = f.read()
                var lines = data.split("\n")
                for line in lines:
                    if line[] == "":
                        break
                    var v = line[].split(" ")
                    hands.append(Hand.create(v[:5]))
                    hands.append(Hand.create(v[5:]))
            return hands
        except:
            return List[Hand]()
    
    @staticmethod
    fn create(v: List[String]) raises -> Hand:
        var cards = List[Card]()
        for s in v:
            cards.append(Card.create(s[]))
        return Hand(cards)


#################### process ####################

fn f(path: String) -> Int:
    var hands = Hand.read(path)
    var counter = 0
    for i in range(0, len(hands), 2):
        if hands[i] > hands[i+1]:
            counter += 1
    return counter

fn main() raises:
    var args = sys.argv()
    var path = args[1]
    print(f(path))

MojoでProject Euler 53

https://projecteuler.net/problem=53

nに対して1000000を超える最小のrとそのときのコンビネーションの値を記憶しておけば、n+1に対するrをO(1)で計算できます。

import sys


#################### process ####################

fn greater_limit(n: Int, prev_min_r: Int, prev_c: Int,
                                            M: Int) -> Tuple[Int, Int]:
    if prev_c == 0:
        var c = 1
        for r in range(1, n//2+1):
            c = c * (n - r + 1) // r
            if c > M:
                return (r, c)
        else:
            return (0, 0)
    else:
        var c = prev_c * n // (n - prev_min_r)
        for r in range(prev_min_r - 1, -1, -1):
            var c1 = c * (r + 1) // (n - r)
            if c1 <= M:
                return (r + 1, c)
            c = c1
        else:
            return (0, 0)   # ここには来ないはず

fn f(N: Int, M: Int) -> Int:
    var s = 0
    var c = 0
    var r = 0
    for n in range(1, N+1):
        var t = greater_limit(n, r, c, M)
        r = t.get[0, Int]()
        c = t.get[1, Int]()
        if r != 0:
            s += n + 1 - 2 * r
    return s

fn main() raises:
    var args = sys.argv()
    var N = atol(args[1])
    var M = atol(args[2])
    print(f(N, M))

MojoでProject Euler 52

https://projecteuler.net/problem=52

各数字が何個あるかを4ビットで表します。

import sys


#################### process ####################

fn count_digits(owned n: Int) -> Int:
    var c = 0
    while n > 0:
        var d = n % 10
        c += 1 << (d * 4)
        n //= 10
    return c

fn f(N: Int) -> Int:
    for E in range(1, 11):
        for x in range(10**(E-1), 10**E//N):
            var c1 = count_digits(x)
            for f in range(2, N+1):
                var c = count_digits(x * f)
                if c1 != c:
                    break
            else:
                return x
    return 0

fn main() raises:
    var args = sys.argv()
    var N = atol(args[1])
    print(f(N))

MojoでProject Euler 51

https://projecteuler.net/problem=51

素数が8つ以上だと、マスクしている数字は3の倍数個でないといけません。なぜなら、そうでなければ数字ごとに3の剰余が変わってしまうから、最大10個できる整数のうち3個は3の倍数になってしまうからです。
素数9つはすぐに出てきましたが、10個は出てきませんね。

import sys


#################### List ####################

trait Printable(CollectionElement, Stringable):
    pass

fn print_list[T: Printable](a: List[T]):
    if a.size > 0:
        var s = "[" + str(a[0])
        for i in range(1, a.size):
            s += ", " + str(a[i])
        s += "]"
        print(s)
    else:
        print("[]")

fn initialize_list[T: CollectionElement](N: Int, init: T) -> List[T]:
    var a = List[T](capacity=N)
    for n in range(N):
        a.append(init)
    return a

fn insert_list[T: CollectionElement](inout v: List[T], e: T, i: Int):
    v.append(e)
    for k in range(len(v) - 1, i, -1):
        v[k] = v[k-1]
    v[i] = e

fn combinations_core[T: CollectionElement](a: List[T],
                                            n: Int, k: Int) -> List[List[T]]:
    if n == 0:
        return List[List[T]](List[T]())
    
    var vs = List[List[T]]()
    for k1 in range(k, len(a) - n + 1):
        var v = combinations_core(a, n-1, k1+1)
        for w in v:
            insert_list(w[], a[k1], 0)
            vs.append(w[])
    return vs

fn combinations[T: CollectionElement](a: List[T], n: Int) -> List[List[T]]:
    return combinations_core(a, n, 0)

fn range_list(first: Int, last: Int, delta: Int) -> List[Int]:
    var v = List[Int]()
    if (first < last and delta > 0) or (first > last and delta < 0):
        for n in range(first, last, delta):
            v.append(n)
    return v

fn reverse_list[T: CollectionElement](a: List[T]) -> List[T]:
    var b = List[T]()
    for i in range(len(a) - 1, -1, -1):
        b.append(a[i])
    return b


#################### library ####################

fn make_prime_table(N: Int) -> List[Bool]:
    var a = initialize_list(N+1, True)
    for p in range(2, N+1):
        if p * p > N:
            break
        elif not a[p]:
            continue
        
        for n in range(p*p, N+1, p):
            a[n] = False
    return a

fn digits(owned n: Int) -> List[Int]:
    var ds = List[Int]()
    while n > 0:
        var d = n % 10
        ds.append(d)
        n //= 10
    return ds

fn number(ds: List[Int]) -> Int:
    var n = 0
    for d in ds:
        n = n * 10 + d[]
    return n


#################### process ####################

fn fill(d: Int, mask: List[Int], ds: List[Int]) -> Int:
    var L1 = len(mask)
    var L2 = len(ds)
    var new_ds = List[Int]()
    var k = 0
    var l = 0
    for i in range(0, L1 + L2):
        if mask[k] == i:
            new_ds.append(d)
            k += 1
        else:
            new_ds.append(ds[l])
            l += 1
    return number(reverse_list(new_ds))

fn collect_primes(mask: List[Int], n: Int, primes: List[Bool]) -> List[Int]:
    var ds = digits(n)
    var E = len(mask) + len(ds)
    var ps = List[Int]()
    for d in range(10):
        if d == 0 and mask[len(mask)-1] == E - 1:
            continue
        var n = fill(d, mask, ds)
        if primes[n]:
            ps.append(n)
    return ps

fn f_each(M: Int, E: Int) -> Int:
    var N = 10**E
    var primes = make_prime_table(N)
    var a = range_list(1, E, 1)
    var min_prime = -1
    for num_mask in range(3, E, 3):
        for mask in combinations(a, num_mask):
            var first = 10**(E-num_mask-1) + 1
            var last = (first - 1) * 10
            for n in range(first, last, 2):
                var ps = collect_primes(mask[], n, primes)
                if len(ps) != M:
                    continue
                if min_prime == -1 or ps[0] < min_prime:
                    min_prime = ps[0]
                    print_list(ps)
    return min_prime

fn f(M: Int) -> Int:
    var E = 4
    while True:
        var ret = f_each(M, E)
        if ret != -1:
            return ret
        E += 1

fn main() raises:
    var args = sys.argv()
    var M = atol(args[1])
    print(f(M))