题目描述
https://codeforces.com/contest/1608/problem/D
简要题意:现在有 $n$ 个骨牌,每个骨牌有左右两边,每边只能是黑色和白色且不能交换,现在给定 $n$ 个骨牌,有些骨牌的的某一些边已经固定了颜色,剩下的位置需要你来染色,现在一种合法的染色方案为将 $n$ 个骨牌染好色之后,存在一个圆排列满足,任亮两个相邻骨牌的相邻的两个边的颜色不同,即第 $i$ 个骨牌的右边和第 $i+1$ 个骨牌的左边的颜色不同,求有多少种染色方案
$n\le 10^5$
Solution
容易发现对于任意一种合法的染色方案,不考虑左右边,黑色和白色的个数都必须是 $n$,令 $cntb$ 和 $cntw$ 分别表示已经固定的白色和黑色的个数,那么现在我们就有一个方案数 $\binom{2n-cntb-cntw}{n-cntw}$
这里面一定包含了所有合法方案,但有一些方案不合法的,我们考虑什么情况下会出现不合法方案,另外我们注意到我们这样染色 $00$ 和 $11$ 的块一定是一样多的,因为 $cntb=cntw=n$ 且 $01$ 和 $10$ 不影响 $cntb$ 和 $cntw$ 的差值
通过观察我们能够发现,只要 $00$ 和 $11$ 的数量大于等于 $1$,方案就一定是合法的,那么我们只要把 $00$ 和 $11$ 出现 $0$ 次的不合法方案减掉即可
时间复杂度 $O(n)$
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
| #include <iostream> #define maxn 100010 #define ll long long using namespace std;
const int p = 998244353;
ll pow_mod(ll x, ll n) { ll s = 1; for (; n; n >>= 1, x = x * x % p) if (n & 1) s = s * x % p; return s; }
int n, cntb, cntw; char s[maxn][2];
ll fac[maxn * 2], inv[maxn * 2]; void init_C(int n) { fac[0] = 1; for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i % p; inv[n] = pow_mod(fac[n], p - 2); for (int i = n - 1; ~i; --i) inv[i] = inv[i + 1] * (i + 1) % p; }
ll C(int n, int m) { return n < m ? 0 : fac[n] * inv[m] % p * inv[n - m] % p; }
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n; init_C(2 * n); for (int i = 1; i <= n; ++i) cin >> s[i]; for (int i = 1; i <= n; ++i) for (int k = 0; k < 2; ++k) if (s[i][k] == 'B') ++cntb; else if (s[i][k] == 'W') ++cntw; if (cntb > n || cntw > n) return cout << 0 << "\n", 0; ll ans = C(2 * n - cntb - cntw, n - cntb), mul = 1; bool ok = 1, f = 0, g = 0; for (int i = 1; i <= n; ++i) { if (s[i][0] == 'W' && s[i][1] == 'W' || s[i][0] == 'B' && s[i][1] == 'B') ok = 0; if (s[i][0] == 'W' && s[i][1] != 'W' || s[i][0] != 'B' && s[i][1] == 'B') f = 1; if (s[i][0] == 'B' && s[i][1] != 'B' || s[i][0] != 'W' && s[i][1] == 'W') g = 1; if (s[i][0] == '?' && s[i][1] == '?') mul = mul * 2 % p; } if (ok) ans = (ans - mul + !f + !g) % p; cout << (ans + p) % p << "\n"; return 0; }
|