Educational Codeforces Round 107 (Rated for Div. 2) G.Chips on a Board 倍增优化DP
Educational Codeforces Round 107 (Rated for Div. 2) G.Chips on a Board 倍增优化DP
题意
给定一个矩阵,保证每一行有一个棋子,两人轮流移动,每次可以选择一个棋子往左移动到任意一个位置,当不可移动时为败。
\(q\)次询问,每次询问表示只看\([L,R]\)列的矩阵时的胜败情况。
\[1 \leq n,m\leq2e5\\
1 \leq c_i \leq m\\
1 \leq q \leq 2e5\\
L \leq R\leq m
\]
分析
容易发现这实际上是在玩Nim游戏。
不妨把\(n\)行的棋子拍成一行来看,问题转化为了,给定左端点和右端点,求所有落在左端点到右端点区间内的所有棋子到左端点距离的异或和。
\(dp[i][j]\)表示区间\([j,j + 2^i)\)的所有棋子到\(j\)的距离的异或和
有
\[dp[i][j] = dp[i - 1][j] + dp[i - 1][j + 2^{i-1}]
\]
若\([j + 2^{i-1},j+2^i)\)有奇数个棋子,则还需要异或上\(2^{i-1}\)
感觉针对异或的,比较精妙的优化,问题在于每次差是2的若干次,只影响一位,因此可以一起统计
有了\(dp[i][j]\)后面就可以用相似方法方便计算了
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rd(){
ll x;
scanf("%lld",&x);
return x;
}
const int maxn = 6e5 + 5;
int _xor[20][maxn];
int main(){
int n = rd();
int m = rd();
vector<int> v(n);
for(int i = 0;i < n;i++)
v[i] = rd();
sort(v.begin(),v.end());
vector<int> sum(m + 1);
for(auto x:v)
sum[x] ^= 1;
for(int i = 2;i <= m;i++)
sum[i] ^= sum[i - 1];
for(int i = 1;i < 20;i++)
for(int j = 1;j <= m && j + (1 << i) <= m;j++){
_xor[i][j] = _xor[i - 1][j] ^ _xor[i - 1][j + (1 << (i - 1))];
if((sum[j + (1 << i) - 1] ^ sum[j + (1 << (i - 1)) - 1]) & 1) _xor[i][j] ^= 1 << (i - 1);
}
int q = rd();
while(q--){
int l = rd();
int r = rd();
int ans = 0;
for(int i = 20 - 1;i >= 0;i--){
int nxt = l + (1 << i);
if(nxt <= r) {
int num = sum[r] ^ sum[nxt - 1];
if(num & 1) ans ^= 1 << i;
ans ^= _xor[i][l];
l = nxt;
}
}
cout << "AB"[ans == 0];
}
}