#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
const int MOD = 1000000007;
// Globalne wektory na silnie i odwrotności silni
vector<long long> fact;
vector<long long> invFact;
// Szybkie potęgowanie modularne (wykorzystywane do Małego Twierdzenia Fermata)
long long power(long long base, long long exp) {
long long res = 1;
base %= MOD;
while (exp > 0) {
if (exp % 2 == 1) res = (res * base) % MOD;
base = (base * base) % MOD;
exp /= 2;
}
return res;
}
// Odwrotność modularna z Małego Twierdzenia Fermata: a^(p-2) mod p
long long modInverse(long long n) {
return power(n, MOD - 2);
}
// 1. Wstępne obliczenia (Precomputing)
void precompute(int max_val) {
// Zabezpieczenie przed wielokrotnym liczeniem w przypadku wielu testów
if (!fact.empty() && fact.size() > max_val) return;
fact.resize(max_val + 1);
invFact.resize(max_val + 1);
fact[0] = 1;
invFact[0] = 1;
for (int i = 1; i <= max_val; ++i) {
fact[i] = (fact[i - 1] * i) % MOD;
}
invFact[max_val] = modInverse(fact[max_val]);
for (int i = max_val - 1; i >= 1; --i) {
invFact[i] = (invFact[i + 1] * (i + 1)) % MOD;
}
}
void solve() {
int n;
cin >> n;
vector<int> a(2 * n);
// Obliczamy silnie dla maksimum 4n kart
precompute(4 * n);
for (int i = 0; i < 2 * n; ++i) {
cin >> a[i];
}
// 2. Analiza tablicy wyników (Sanity Check)
bool possible = true;
for (int i = 0; i < 2 * n; ++i) {
int next_i = (i + 1) % (2 * n);
// Sprawdzenie nieprawidłowych skoków (różnica między sąsiadami nie może być np. > 1)
if (abs(a[i] - a[next_i]) > 1) {
possible = false;
}
// Wartości muszą być z zakresu 0, 1, 2
if (a[i] < 0 || a[i] > 2) {
possible = false;
}
}
// Jeśli warunki brzegowe nie są spełnione, gra jest niemożliwa
if (!possible) {
cout << 0 << "\n";
return;
}
// 3. Identyfikacja kluczowych graczy i ról
int fixed_cards = 0;
int fixed_players = 0;
long long multiplier = 1;
bool all_twos = true, all_zeros = true, all_ones = true;
for (int val : a) {
if (val != 2) all_twos = false;
if (val != 0) all_zeros = false;
if (val != 1) all_ones = false;
}
if (all_twos) {
// Zawsze 2 punkty dla Algorytmików. Jeden z nich musi mieć dwie najwyższe karty.
fixed_cards = 2;
fixed_players = 1;
multiplier = n;
}
else if (all_zeros) {
// Zawsze 0 punktów dla Algorytmików. Jeden z przeciwników ma obie najwyższe karty.
fixed_cards = 2;
fixed_players = 1;
multiplier = n;
}
else if (all_ones) {
// Zawsze 1 punkt. Gra jest zbalansowana.
fixed_cards = 2;
fixed_players = 2;
// Mamy n Algorytmików i n przeciwników. Kombinacji wyboru jest n * n.
multiplier = (1LL * n * n) % MOD;
}
else {
// W tablicy 'a' występują zmiany (np. 1 -> 2, 0 -> 1).
int transitions = 0;
for (int i = 0; i < 2 * n; ++i) {
int next_i = (i + 1) % (2 * n);
if (a[i] != a[next_i]) {
transitions++;
}
}
fixed_cards = 2;
fixed_players = 2;
multiplier = 1; // Pozycje są sztywno zdeterminowane przez indeksy zmian
// Jeśli liczba przejść nie pasuje do zasad gry o 2 lewy
if (transitions != 2 && transitions != 4) {
possible = false;
}
}
// Ponowne sprawdzenie possible po weryfikacji przejść
if (!possible) {
cout << 0 << "\n";
return;
}
// 4. Obliczenie ostatecznej liczby układów (Kombinatoryka)
long long result = 0;
int remaining_cards = 4 * n - fixed_cards;
int remaining_players = 2 * n - fixed_players;
// Sprawdzamy, czy liczba pozostałych kart idealnie starcza, aby każdy dostał po 2
if (remaining_cards == 2 * remaining_players && remaining_cards >= 0) {
// Wzór: (4n - k)!
result = fact[remaining_cards];
// Dzielimy przez (2!)^(liczba pozostałych graczy)
long long denominator = power(2, remaining_players);
long long inv_denominator = modInverse(denominator);
// Mnożymy: (4n-k)! * odwrotność mianownika * ilość sposobów ułożenia ról
result = (result * inv_denominator) % MOD;
result = (result * multiplier) % MOD;
} else {
result = 0; // Ułożenie nie istnieje
}
cout << result << "\n";
}
int main() {
// Optymalizacja I/O
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int t;
if (cin >> t) {
while (t--) {
solve();
}
}
return 0;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | #include <iostream> #include <vector> #include <cmath> using namespace std; const int MOD = 1000000007; // Globalne wektory na silnie i odwrotności silni vector<long long> fact; vector<long long> invFact; // Szybkie potęgowanie modularne (wykorzystywane do Małego Twierdzenia Fermata) long long power(long long base, long long exp) { long long res = 1; base %= MOD; while (exp > 0) { if (exp % 2 == 1) res = (res * base) % MOD; base = (base * base) % MOD; exp /= 2; } return res; } // Odwrotność modularna z Małego Twierdzenia Fermata: a^(p-2) mod p long long modInverse(long long n) { return power(n, MOD - 2); } // 1. Wstępne obliczenia (Precomputing) void precompute(int max_val) { // Zabezpieczenie przed wielokrotnym liczeniem w przypadku wielu testów if (!fact.empty() && fact.size() > max_val) return; fact.resize(max_val + 1); invFact.resize(max_val + 1); fact[0] = 1; invFact[0] = 1; for (int i = 1; i <= max_val; ++i) { fact[i] = (fact[i - 1] * i) % MOD; } invFact[max_val] = modInverse(fact[max_val]); for (int i = max_val - 1; i >= 1; --i) { invFact[i] = (invFact[i + 1] * (i + 1)) % MOD; } } void solve() { int n; cin >> n; vector<int> a(2 * n); // Obliczamy silnie dla maksimum 4n kart precompute(4 * n); for (int i = 0; i < 2 * n; ++i) { cin >> a[i]; } // 2. Analiza tablicy wyników (Sanity Check) bool possible = true; for (int i = 0; i < 2 * n; ++i) { int next_i = (i + 1) % (2 * n); // Sprawdzenie nieprawidłowych skoków (różnica między sąsiadami nie może być np. > 1) if (abs(a[i] - a[next_i]) > 1) { possible = false; } // Wartości muszą być z zakresu 0, 1, 2 if (a[i] < 0 || a[i] > 2) { possible = false; } } // Jeśli warunki brzegowe nie są spełnione, gra jest niemożliwa if (!possible) { cout << 0 << "\n"; return; } // 3. Identyfikacja kluczowych graczy i ról int fixed_cards = 0; int fixed_players = 0; long long multiplier = 1; bool all_twos = true, all_zeros = true, all_ones = true; for (int val : a) { if (val != 2) all_twos = false; if (val != 0) all_zeros = false; if (val != 1) all_ones = false; } if (all_twos) { // Zawsze 2 punkty dla Algorytmików. Jeden z nich musi mieć dwie najwyższe karty. fixed_cards = 2; fixed_players = 1; multiplier = n; } else if (all_zeros) { // Zawsze 0 punktów dla Algorytmików. Jeden z przeciwników ma obie najwyższe karty. fixed_cards = 2; fixed_players = 1; multiplier = n; } else if (all_ones) { // Zawsze 1 punkt. Gra jest zbalansowana. fixed_cards = 2; fixed_players = 2; // Mamy n Algorytmików i n przeciwników. Kombinacji wyboru jest n * n. multiplier = (1LL * n * n) % MOD; } else { // W tablicy 'a' występują zmiany (np. 1 -> 2, 0 -> 1). int transitions = 0; for (int i = 0; i < 2 * n; ++i) { int next_i = (i + 1) % (2 * n); if (a[i] != a[next_i]) { transitions++; } } fixed_cards = 2; fixed_players = 2; multiplier = 1; // Pozycje są sztywno zdeterminowane przez indeksy zmian // Jeśli liczba przejść nie pasuje do zasad gry o 2 lewy if (transitions != 2 && transitions != 4) { possible = false; } } // Ponowne sprawdzenie possible po weryfikacji przejść if (!possible) { cout << 0 << "\n"; return; } // 4. Obliczenie ostatecznej liczby układów (Kombinatoryka) long long result = 0; int remaining_cards = 4 * n - fixed_cards; int remaining_players = 2 * n - fixed_players; // Sprawdzamy, czy liczba pozostałych kart idealnie starcza, aby każdy dostał po 2 if (remaining_cards == 2 * remaining_players && remaining_cards >= 0) { // Wzór: (4n - k)! result = fact[remaining_cards]; // Dzielimy przez (2!)^(liczba pozostałych graczy) long long denominator = power(2, remaining_players); long long inv_denominator = modInverse(denominator); // Mnożymy: (4n-k)! * odwrotność mianownika * ilość sposobów ułożenia ról result = (result * inv_denominator) % MOD; result = (result * multiplier) % MOD; } else { result = 0; // Ułożenie nie istnieje } cout << result << "\n"; } int main() { // Optymalizacja I/O ios_base::sync_with_stdio(false); cin.tie(NULL); int t; if (cin >> t) { while (t--) { solve(); } } return 0; } |
English