Official

D - Peaceful Teams Editorial by en_translator


This problem can be solved by adding players to a team one by one, or deciding the members of each team at once.

1. adding players to a team one by one, \(O(N\times N!)\) time

Assume that we have divided the \(1\)-th, \(2\)-nd, \(\ldots\), and \((i-1)\)-th team into \(T\) teams so that no incompatible pairs of players belong to the same team.

In order to add the \(i\)-th player, we can either add the \(i\)-th player to at most \(T\) existing teams, or (if there are less than \(T\) teams) making a new team with only \(i\) team.

By writing this process with a recursive function, this can be solved in a total of \(O(N\times N!)\) times.

By storing the players incompatible with the \(i\)-th player and the current division in consecutive \(N\operatorname{bits}\), the time complexity can be reduced to \(O(N/w\times N!)\) time.

The following is a sample code.

#include <iostream>
#include <vector>

int main() {
    using namespace std;
    unsigned N, T, M;
    cin >> N >> T >> M;

    // j-th bit of hate[i] is 1 ⟹ i-th and j-th players are incompatible (0-indexed)
    vector<unsigned> hate(N);
    for(unsigned i{}, a, b; i < M; ++i){
        cin >> a >> b;
        hate[--b] |= 1U << --a;
    }

    // Print the result of the recursive function
    cout << [dfs{[N, T, &hate](auto&& f, vector<unsigned>& teams, unsigned now) -> unsigned {
        // OK if there are T teams so far when all the N players are inspected
        if(now == N)
            return size(teams) == T;

        unsigned ans{};

        // Add the now-th player to an existing team
        for(auto&& team : teams)
            // If nobody in the team is incompatible with the now-th
            if(!(team & hate[now])){
                team ^= 1U << now;
                ans += f(f, teams, now + 1);
                team ^= 1U << now;
            }

        // If new team can be made, make a new one
        if(size(teams) < T){
            teams.emplace_back(1U << now);
            ans += f(f, teams, now + 1);
            teams.pop_back();
        }

        return ans;
    }}, T]{
        vector<unsigned> team;
        team.reserve(T);
        return dfs(dfs, team, 0);
    }() << endl;

    return 0;
}

2. deciding the members of each team at once, \(O(3^NT)\) time

Let \(\operatorname{dp}[S][t]\coloneqq\bigl(\) the number of ways to divide the players in \(S\) into \(t\) teams without putting incompatible players into one team \(\bigr)\).

The DP table can be filled by scanning the DP (Dynamic Programming) table in ascending order of \(S\) (considered as a non-negative base-2 integers) and brute-forcing the members of the team that the player corresponding to the minimum integer in \(\{1,2,\ldots,N\}\setminus S\) belongs. (One can also use memorized recursion).

By enumerating the possible teams without incompatible pairs beforehand in a total of \(O(2^N\operatorname{poly}(N))\) time, the complexity is \(O(3^NT)\).

The following is a sample code.

#include <iostream>
#include <vector>
#include <bitset>

int main() {
    using namespace std;
    unsigned N, T, M;
    cin >> N >> T >> M;

    vector<unsigned> hate(N);
    for(unsigned i{}, a, b; i < M; ++i){
        cin >> a >> b;
        hate[--b] |= 1U << --a;
    }

    // possible_team[S] := The team with players in S does not have an incompatible pair
    // O(2^N N^2/w) time
    bitset<1024> possible_team;
    for(unsigned i{}; i < 1U << N; ++i){
        unsigned m{};
        for(unsigned j{}; j < N; ++j)if(1U & (i >> j))m |= hate[j];
        if(!(i & m))
            possible_team[i] = true;
    }

    vector dp(1U << N, vector<unsigned>(T + 1));
    dp.front().front() = 1;
    for(unsigned i{}; i < 1U << N; ++i)
        // brute-force over all possible teams that the remaining player with the minimum integer belongs
        for(unsigned c{i + 1 | i}, j{c}; j < 1U << N; ++j |= c)
            if(possible_team[j ^ i])
                for(unsigned k{}; k < T; ++k)
                    dp[j][k + 1] += dp[i][k];

    cout << dp.back().back() << endl;

    return 0;
}

posted:
last update: