CSES - Datatähti 2025 alku - Results
Submission details
Task:Kortit I
Sender:wolruso
Submission time:2024-11-09 13:24:01 +0200
Language:Rust (2021)
Status:COMPILE ERROR

Compiler report

error[E0428]: the name `TestInfo` is defined multiple times
   --> input/code.rs:292:1
    |
1   | pub struct TestInfo {
    | ------------------- previous definition of the type `TestInfo` here
...
292 | struct TestInfo {
    | ^^^^^^^^^^^^^^^ `TestInfo` redefined here
    |
    = note: `TestInfo` must be defined only once in the type namespace of this module

error: aborting due to previous error

For more information about this error, try `rustc --explain E0428`.

Code

pub struct TestInfo {
    pub num_cards: usize,
    pub p1_points: usize,
    pub p2_points: usize,
}

pub fn initial_method(
    num_cards: usize,
    num_points_p1: usize,
    num_points_p2: usize,
) -> Option<(Vec<usize>, Vec<usize>)> {
    // 5 4 3 2 1
    // 5 -> just reduces the problem to (n - 1)
    // anything else -> means that card can not be played against the enemy's such card, meaning a
    // tie won't result there, meaning one player will win there, meaning p2 will win there, as
    // their going to have a n to place there
    // -> Thus it is not possible to have a (n - 1) tie. Instead, an n or (n - 2) tie is the best
    // possible, and in the latter case, both players will have to get one point
    //
    // In fact, all ties, a count k of them, just cause the problem to become (n - k) instead
    // (regardless of the place of the ties)

    // 5 4 3 2 1  4, 1
    // 1 5 4 3 2
    //
    // 5 4 3 2 1  3, 2
    // 1 3 5 4 2
    //
    // 5 4 3 2 1  1, 4
    // 4 3 2 1 5
    //
    // 5 4 3 2 1
    // 4 5 1 3 2
    //
    // I.e.: neither 1, nor (n - 1), is ALWAYS a valid option
    //
    // Picking 1 < x < n - 1 as the first card signifies:
    // - that card is no more available to be picked for e(x - 1)
    // - that card is no more available to be picked for e(x + 1)
    //
    // 5 4 3 2 1
    // 3 5 4 1 2
    //
    // 5 4 3 2 1
    // 2 5 4 3 1
    //
    // Whenever a card y(a > b) is picked as a response to e(b), it reduces the amount of cards
    // y(x >= a) available to win against all cards e(x <= a - 2).
    // It does not affact the ability for winning against cards e(x >= a - 2)
    // 1
    // b
    //
    // Actually, a better way to represent this would be by saying that:
    // Whenever a card y(a > b + 1) is picked as a response to e(b), it makes the card y(b + 1)
    // useless. Or, actually, if b > 1 (as would generally be the case), it causes a chain
    // reaction, where yes, the card y(b + 1) can be played against cards e(x < b), but now there's
    // b + 1 (of which one is the card 1, which can not be used to win, so really, there's only b
    // cards that matter in this sense) cards of player Y available to be played against b - 1 cards
    // of the enemy. There is a mismatch, and thus one of those cards will inevitable go to waste,
    // i.e. be unable to be used to win an exchange, and must thus be used to lose instead.
    //
    // 5 4 3 2 1
    // 1 2 5 4 3
    //
    // 5 4 3 2 1
    // 1 2 4 3 5
    //
    // And along with the effect mentioned in the previous paragraph, there is also the effect that
    // there will now be one card fewer for winning against cards e(x > b), one card less than
    // there are those cards of the enemy. Thus the one card y(x > b + 1) that doesn't exist will
    // instead be replaced by the wasted card y(x <= b).
    //
    // Actually, the previous paragraph mentioned in the previous paragraph is entirely incorrect.
    //
    // When a card y(a > b) is picked to win against a card e(b), the cards e(e) are split into two
    // groups: e(x > b) and e(x <= b). The former group doesn't get any smaller as a result of the
    // exchange, but the latter does, as e(b) is included in it. If a == b + 1, then the group of
    // cards y(c > b + 1) that can be played against the group of cards e(x > b) does not get any
    // smaller. But if a > b + 1, the group y(c > b + 1) does get smaller by one card. This means
    // that the amount of times it is possible to win against cards e(x > b) is reduced by one.
    //     The same applies for picking a card y(a < b) against a card e(b), except the relations are
    // reversed.
    //
    // 5 4 3 2 1
    // 1 3 2 4 5
    //
    // 5 4 3 2 1
    // 4 3 2 1 5
    //
    // 5 4 3 2 1
    // 3 5 2 1 4
    //
    // e(b) => y(a < b)
    // e(c < a <=> c < b - 1) can be won against with the cards y(d >= a). That group e(c) of cards does
    // not get any smaller but the group y(d) does.
    //
    //
    // Formal Approach:
    //
    // The game can be thought of as a sequence of exchanges. The game will then have a separate,
    // distinct state at each such element of the sequence: a state consisting of the set of cards
    // e(x) and y(x).
    //     Since in the first part of the exercise, the particular game doesn't matter as long as
    // it ends up with the right score totals, it doesn't matter in which order the cards are
    // represented. Thus it would be helpful to use a convention where the exchanges are ordered
    // based on cards e(x) in descending order.
    //     The above inferences can be formulated thusly. At each element of the sequence of
    // exchanges, S(n), regardless of how the game has been played by both players up to this
    // point, regardless of which elements the sets E(n) and Y(n) hold, the following holds true.
    //     The set E(n) can be split into two sets, e(a > o(n)) and e(b <= o(n)). In S(n + 1), if such
    // a state exists, i.e. n < N, e(a) will be equally large, while e(b) will have decreased in
    // size by one, compared to the state S(n), as that set includes the element e(o(n)).
    //     It is relevant to define one more set, this time for Y(n): y(a > o(n) + 1). This set
    // describes the elements of Y(n) that can be played against e(a) to win. If t(n) == o(n) + 1, then
    // the set y(a) does not decrease in size, and thus will remain as equal with e(a) as it was in
    // the previous state S(n - 1), provided of course, that n > 0. If, on the other hand, t(n) > o(n) + 1,
    // then it does belong in the set y(a), and thus the size of that set decreases. This means that one
    // fewer card in e(a) can be won against.
    //     But what is the effect on the possibilities for losing against E(a > n)? The only
    // elements E(n) that might possibly be affected by this are e(c > o(n) + 1). Of course the
    // size of e(c) remains constant across state boundaries, and the number of elements y(c <=
    // o(n) + 1)
    //
    // The algorithm:
    // It would generate the game state, one exchange at a time, starting from the previously
    // described order. Before continuing with producing an exchange sequence for the wins and
    // losses though, the algorithm would calculate the number of ties and produce the appropriate
    // sequence of the appropriate length to describe the tie exchanges. At the end the algorithm
    // could then preprend that sequence to the generated one.
    //     At each exchange, the algorithm would produce such an exchange, that it produced a win
    // for p2 if there are still wins needed to be obtained for that player, and otherwise one
    // for p1. The exhange would be made such, that it would not lower the maximum amount of points
    // each player can still obtain beyond the point total they are to acquire. The algorithm would
    // only need to consider the point maximums separately for each player, as there is no link
    // between them.
    //
    // Thus the only problems remaining are:
    // - Edge cases at cards n and 1
    //     - 1 is the only card that can not be used for winning, and thus choosing that card
    //     for a losing option does not atually reduce My(n)
    //     - n is the only card that can not be used to lose, and thus choosing that card for a
    //     winning option does not actually reduce Me(n)
    // - How to avoid accidentally causing a tie, as that is illegal now that the ties have already
    // been taken care of before?
    //     - As E(0) == Y(0), and each exchange is either a win or a loss, which means o(n) !=
    //     t(n), - no nvm: S(1) = (1, 2), S(2) = (2, 1), then S(3) must be (3, 3).
    // IMPORTANT: e(m) and e(l) here are for the state at the start of the next exchange, S(n + 1)!
    // - How does choosing a winning card affect maximum losing plays left?
    //     - If t(n) < e(m), then Me(n) decreases
    //     - If t(n) >= e(m), then Me(n) does not change
    // - How does choosing a losing card affect maximum winning playes left?
    //     - If t(n) > e(l), then My(n) decreases
    //     - If t(n) <= e(l), then My(n) does not change
    //
    //  So basically, in the above two cases, if the card t(n) could not be used for winning or
    //  losing in S(n + 1) anyway, then there would naturally be no reduction in My(n + 1) or Me(n
    //  + 1)
    // (Could consider the above 2 by considering every possible state + exchange combination and
    // then finding regularities in the effect)
    //
    // The algoritm proceeds in descending order from e(m) towards e(l). At S(1), there are 2
    // options: t(1) = n - 1 (if p1 = n - 1) or t(1) = 1 (if p2 = n - 1). If neither, t(1) can be
    // either.
    // S(2): If pc2 < p2, then t(2) = n - 1 + 1 = n. Else, t(2) = n - 1 - 1 = n - 2.
    // S(3): If pc2 < p2, then t(3) = n - 2 + 1 = n - 1. Except, this card might've been selected
    // for S(1) before. In that case, it is not possible anymore to win this. Nvm, if t(1) = n - 1,
    // then p1 = n - 1, which means S(2) should be a loss anyway, and the only win should be at
    // S(n).
    //     Thus n - 1 is actually a secure choice here. If instead pc1 < p1, then t(3) = n - 2 - 1
    // = n - 3.
    //
    //
    // General rule
    //
    // The cases of p1 = n - 1 and p2 = n - 1 can be thought of as a special case for which its own logic exists.
    // p1 case: t(i) = n - i, for every case i != n. t(n) = n.
    // p2 case: t(i) = n - i + 2, for every case i != 1. t(1) = 1.
    //
    // For every other case, the wins will be generated first, and then the losses. (Apart from
    // S(1) of course.) For each win, t(i) = n - i + 2. For each loss, t(i) = n - i.
    // E:    7 6 5 4 3 2 1
    // Y:    1 7 6 3 2 5 4
    // Me: 6 4 3 2 1 0 0 0
    // My: 6 6 5 4 3 2 1 0
    // Note: The "base" M() reduction comes into play only if that exchange was indeed winnable or
    // losable
    //
    // Note: At S(6) and S(7), the order of the 5 and 4 don't matter.
    //
    // Walkthrough:
    // c = 7, p1 = 3, p2 = 4
    // t(1) = 1, as p1 < n - 1 and p2 < n - 1
    // t(2) = n - 2 + 2 = n, as pc2 < p2 - 2, the winning logic
    // t(3) = n - 3 + 2 = n - 1, as pc2 < p2 - 2, the winning logic
    //
    // Normally, the winning would continue here, with t(4) = n - 2. But since pc2 = 2, and both S(n -
    // 1) and S(n) must be wins, pc2 < p2 - 2 must hold for another win to be generated. Since p2 -
    // 2 = 4, and 2 == 2, the algorithm must switch to losses here.
    //
    // t(4) = n - 4 as pc2 == p2 - 2, the losing logic
    // t(5) = n - 5, the losing logic
    // The logic switches here. The lost 2 exchanges can be nothing else apart from a win, so the
    // last two cards should just be played here in an arbitrary order. The two cards remaining are
    // always those that would've been played in the first two exchanges that grant a loss (after
    // S(1) of course) to generate a win instead. In this case, 5 and 4.
    // t(6) = 5, ending logic
    // t(7) = 4, ending logic
    //
    // p1 = 5, p2 = 4
    // 9 8 7 6 5 4 3 2 1
    // 1 9 8 5 4 3 2 7 6
    // w(n) = l(n - 2)
    //
    // p1 = 9, p2 = 3
    // 12 11 10 9 8 7 6 5 4 3 2  1
    // 1  12 9  8 7 6 5 4 3 2 11 10
    // Note: S(n) is a guaranteed win, as it can't be a tie, and no card is less than 1. Thus it is
    // necessary to compare pc2 to p2 - 1, not just p2.

    if num_points_p1 > (num_cards - 1)
        || num_points_p2 > (num_cards - 1)
        || (num_points_p1 + num_points_p2) > num_cards
        || (!(num_points_p1 == 0 && num_points_p2 == 0)
            && (num_points_p1 == 0 || num_points_p2 == 0))
    {
        return None;
    }
    let num_ties = num_cards - num_points_p1 - num_points_p2;
    let mut p2_cards = Vec::new();
    // let mut maximum_p1_points = num_cards - 1;
    // let mut maximum_p2_points = num_cards - 1;
    let mut current_p1_points = 0;
    let mut current_p2_points = 0;
    let mut cards = Vec::new();
    for i in (1..=num_cards).rev() {
        cards.push(i);
    }
    // means that p2 = c - ties - p1
    if num_points_p2 == 1 {
        // i = num_ties is still a tie, and i = num_ties + 1 is S(n)
        for i in ((num_ties + 2)..=num_cards).rev() {
            p2_cards.push(i - 1);
        }
        p2_cards.push(num_cards);
        for i in (1..=num_ties).rev() {
            p2_cards.push(i);
        }
        return Some((cards, p2_cards));
    } else {
        p2_cards.push(num_ties + 1);
        current_p1_points += 1;
    }
    // This is relevant in the case of c = 3, p1 = 1, p2 = 2
    let mut switch_to_losses = 2;
    let mut switch_to_losses_happened = false;
    for i in ((num_ties + 3)..num_cards).rev() {
        if current_p2_points < (num_points_p2 - 2) {
            p2_cards.push(i + 1);
            current_p2_points += 1;
        } else if current_p1_points < num_points_p1 {
            if !switch_to_losses_happened {
                switch_to_losses = i;
                switch_to_losses_happened = true;
            }
            p2_cards.push(i - 1);
            current_p1_points += 1;
        } else {
            return None;
        }
        // maximum_p1_points -= 1;
        // maximum_p2_points -= 1;
        // if maximum_p1_points < (num_points_p1 - current_p1_points) {
        //     return None;
        // }
        // if maximum_p2_points < (num_points_p2 - current_p2_points) {
        //     return None;
        // }
    }
    // There will always be at least two wins for p2, because otherwise the answer is already
    // generated before and this logic is never reached. If there are only two wins, they will both happen
    // here
    p2_cards.push(switch_to_losses + 1);
    p2_cards.push(switch_to_losses);
    for i in (1..=num_ties).rev() {
        p2_cards.push(i);
    }
    Some((cards, p2_cards))
}

use std::io::stdin;

struct TestInfo {
    num_cards: usize,
    p1_points: usize,
    p2_points: usize,
}

fn main() {
    let mut l = String::new();
    stdin().read_line(&mut l).unwrap();
    let num_tests: usize = l.trim().parse().unwrap();
    let mut tests: Vec<TestInfo> = Vec::with_capacity(num_tests);
    for _ in 0..num_tests {
        let mut tl = String::new();
        stdin().read_line(&mut tl).unwrap();
        let mut nums = tl.trim().split(' ');
        let c: usize = nums.next().unwrap().parse().unwrap();
        let p1: usize = nums.next().unwrap().parse().unwrap();
        let p2: usize = nums.next().unwrap().parse().unwrap();
        tests.push(TestInfo {
            num_cards: c,
            p1_points: p1,
            p2_points: p2,
        });
    }
    for i in 0..num_tests {
        let t = &tests[i];
        let answer_tmp = initial_method(t.num_cards, t.p1_points, t.p2_points);
        let answer = match answer_tmp {
            None => {
                println!("NO");
                continue;
            }
            Some(a) => {
                println!("YES");
                a
            }
        };
        let p1_cards = answer
            .0
            .into_iter()
            .map(|c| c.to_string())
            .collect::<Vec<String>>()
            .join(" ");
        let p2_cards = answer
            .1
            .into_iter()
            .map(|c| c.to_string())
            .collect::<Vec<String>>()
            .join(" ");
        println!("{}", p1_cards);
        println!("{}", p2_cards);
    }
}