传送门
首先就给我们一个玩游戏的策略,很明显知道 SG(x)=x ,想要 Alice 赢,区间SG(x)异或和不为0,我们可以直接处理前缀异或和,然后询问区间不同的异或对,典型的带修莫队,注意细节就可以了。注意答案会爆 int 。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
///SG(x)=x;
int num[maxn], a[maxn];
int cnt[maxn * 20], len;
ll sum, ans[maxn];
int pos[maxn], base;
int cntq, cntc;
struct nodeQ {
int l, r; ///查询区间
int t; ///查询前修改的次数
int id; ///询问编号
} Q[maxn];
int C[maxn]; //改变位置
inline bool cmp(nodeQ a, nodeQ b) {
if (pos[a.l] == pos[b.l]) {
if (pos[a.r] == pos[b.r])
return a.t < b.t;
return a.r < b.r;
}
return a.l < b.l;
}
void add(int x) {
len++, cnt[a[x]]++;
sum = sum + len - cnt[a[x]];
}
void del(int x) {
sum = sum - (len - cnt[a[x]]);
len--, cnt[a[x]]--;
}
void work(int t, int i) {
///交换 pos 和 pos+1 ,只会改变 pos这个位置的前缀异或和
if (Q[i].l <= C[t] && C[t] <= Q[i].r) {
ll x = a[C[t]];
ll y = a[C[t] - 1] ^num[C[t] + 1];
sum = sum - (len - cnt[x]);
len--, cnt[x]--;
len++, cnt[y]++;
sum = sum + len - cnt[y];
}
a[C[t]] = a[C[t] - 1] ^ num[C[t] + 1];
swap(num[C[t]], num[C[t] + 1]);
}
void Moqueue() {
int l = 0, r = -1, t = 0;
for (int i = 1; i <= cntq; i++) {
while (l < Q[i].l) del(l++);
while (l > Q[i].l) add(--l);
while (r < Q[i].r) add(++r);
while (r > Q[i].r) del(r--);
while (t > Q[i].t) work(t--, i);
while (t < Q[i].t) work(++t, i);
ans[Q[i].id] = sum;
}
for (int i = 1; i <= cntq; i++)
printf("%lld\n", ans[i]);
}
void init() {
len = sum = cntq = cntc = 0;
memset(cnt, 0, sizeof(cnt));
}
int main() {
int n, m;
while (scanf("%d %d", &n, &m) != EOF) {
init();
base = pow(n, 0.6666666666);
for (int i = 1; i <= n; i++) {
scanf("%d", &num[i]);
a[i] = num[i] ^ a[i - 1];
pos[i] = i / base + 1;
}
int op, l, r, s;
for (int i = 1; i <= m; i++) {
scanf("%d", &op);
if (op == 1) {
scanf("%d %d", &l, &r);
cntq++;
Q[cntq] = nodeQ{l - 1, r, cntc, cntq};
} else {
scanf("%d", &s);
cntc++;
C[cntc] = s;
}
}
sort(Q + 1, Q + 1 + cntq, cmp);
Moqueue();
}
return 0;
}