N×N 棋盤(N ≤ 1000),每格黑/白。Q 次操作(Q ≤ 100,000),操作 (x,y) 翻轉第 x 行和第 y 列所有棋子(交點 (x,y) 翻兩次等價不翻)。求最終白棋數。多測試用例(T ≤ 20)。
每格 (i,j) 受影響條件:操作的行 = i 或列 = j。
總翻轉次數 = rowFlip[i] + colFlip[j]((i,j) 被計算兩次,但模 2 下 2 ≡ 0,不影響奇偶)。
最終顏色 = 初始顏色 ⊕ (rowFlip[i] + colFlip[j] % 2)。
rowFlip[N+1], colFlip[N+1] 初始化 0rowFlip[x] ^= 1; colFlip[y] ^= 1;(異或 1 實現奇偶切換)(i,j):
flips = rowFlip[i] ^ colFlip[j](異或等價模 2 加法)flips == 1:顏色翻轉,初始='b' → 白,初始='w' → 黑flips == 0:顏色不變,初始='w' → 白
#include
using namespace std;
const int MAXN = 1005;
char board[MAXN][MAXN];
bool rowFlip[MAXN], colFlip[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T; cin >> T;
for (int tc = 1; tc <= T; ++tc) {
int N; cin >> N;
for (int i = 1; i <= N; ++i) {
cin >> (board[i] + 1); // 從下標 1 開始存,跳過 board[i][0]
}
fill(rowFlip+1, rowFlip+N+1, false);
fill(colFlip+1, colFlip+N+1, false);
int Q; cin >> Q;
while (Q--) {
int x, y; cin >> x >> y;
rowFlip[x] = !rowFlip[x];
colFlip[y] = !colFlip[y];
}
int white = 0;
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
bool flip = rowFlip[i] ^ colFlip[j];
if (flip) {
if (board[i][j] == 'b') ++white;
} else {
if (board[i][j] == 'w') ++white;
}
}
}
cout << "Case #" << tc << ": " << white << '\n';
}
return 0;
}
rowFlip/colFlip 只需 0/1,用 bool 或 bitset 節省內存與時間a ^ b 等價 (a+b)%2,無分支更快ios::sync_with_stdio(false); cin.tie(nullptr);,字符串讀取 cin >> (board[i]+1) 避免逐字符讀取board[i]+1 使每行從索引 1 開始,遍歷時連續訪問(x,y) 翻兩次等價不翻 → 需用異或/模 2 自然處理int 計數導致溢出或慢 → 只需奇偶,用 boolsync_with_stdio(false)MAXN+5,下標 1..NHDU 4146 是「二維差分/翻轉奇偶性」的經典應用。核心技巧:將二維翻轉分解為行翻轉 + 列翻轉的獨立奇偶標記,利用 rowFlip[i] ^ colFlip[j] 在 O(1) 計算任意格子最終狀態。避免模擬每次操作翻轉整行整列(O(Q×N) 會超時)。此類「批量翻轉/區間翻轉求最終狀態」問題,奇偶性分解是通用解法。