44 分求 de,大致思路是从下往上讨论,为了方便处理每次处理 2R 次方长度的区间,然后和之前答案完全固定的区间做合并,具体的比较部分都写了注释,看了一下午不知道哪里有问题
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
int x = 0, f = 1;
char ch = getchar();
//一直没有读到数字,注意处理负号
while (ch < '0' || ch > '9') {
if(ch == '-') {
f = -1;
}
ch = getchar();
}
//一位位读进数字
while (ch >= '0' && ch <= '9') {
//等价于 x * 10 + ch - '0'
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
}
//符号乘上数字
return x * f;
}
const int maxn = 100005;
const int maxp = 20;
struct SegmentTree{
int l;
int r;
int winner;
int w_val;
int sum;
} tr[maxn << 3];
int n, m, K;
int a[maxn], val[maxn];
int X[4];
int d[maxp][maxn << 1];
char D[maxp][maxn << 1];
int tr_id[maxn];
int q[maxn];
int ans[maxn];
int lose_sum, nxt_pos;
int lst;
void pre() {
for (int i = 1; i <= n; ++i) {
val[i] = a[i] ^ X[i % 4];
}
}
void push_up(int x) {
tr[x].sum = tr[x << 1].sum + tr[x << 1 | 1].sum;
}
void build(int x, int l, int r, int offset) {
tr[x].l = l;
tr[x].r = r;
tr[x].winner = 0;
tr[x].w_val = 0;
if (l == r) {
tr[x].sum = l + offset;
tr_id[l + offset] = x;
return;
}
int mid = (l + r) >> 1;
build(x << 1, l, mid, offset);
build(x << 1 | 1, mid + 1, r, offset);
push_up(x);
}
void ins(int x, int val, int p, int offset) {
tr[x].winner = tr[x].sum;
tr[x].w_val = val;
int R = 1;
while (x != 1) {
//父亲节点
int prt = x >> 1;
int now_p = (p + 1) / 2;
//编号小,左儿子
if ((prt << 1) == x) {
//当前是擂主
if(d[R][now_p] == 0) {
//胜出,那么右儿子所有都不可能取胜
if (tr[x].w_val >= R) {
tr[prt].winner = tr[x].winner;
tr[prt].w_val = tr[x].w_val;
nxt_pos = tr[prt].r + offset;
lose_sum += tr[prt << 1 | 1].sum;
} else { //失败,那么右儿子情况未知,只能减去当前的价值
lose_sum += tr[x].winner;
break;
}
} else {
//不是擂主的话,因为是左儿子,右边都是未知信息,跳过
break;
}
} else { //编号大,右儿子
//当前是擂主
if (d[R][now_p] == 1) {
//作为擂主胜出,那么左儿子之前的胜者不可能再取胜
if (tr[x].w_val >= R) {
tr[prt].winner = tr[x].winner;
tr[prt].w_val = tr[x].w_val;
lose_sum += tr[prt << 1].winner;
} else { //否则,左儿子之前的胜者将继续比赛
tr[prt].winner = tr[prt << 1].winner;
tr[prt].w_val = tr[prt << 1].w_val;
lose_sum += tr[x].winner;
}
} else { //不是擂主的话,说明左边之前作为擂主输了,继续向上
tr[prt].winner = tr[x].winner;
tr[prt].w_val = tr[x].w_val;
}
}
//轮数增加
x = prt;
p = now_p;
++R;
}
}
void solve(int l, int r, int R) {
if (d[R][1] == 0) {
if (val[lst] >= R) {
for (int i = l; i <= r; ++i) {
ans[i] = lst;
}
return;
}
}
// printf("R: %lld, lst: %lld\n", R, lst);
build(1, 1, r - l + 1, l - 1);
int sum = (r + l) * (r - l + 1) / 2;
// printf("sum: %lld\n", sum);
lose_sum = 0;
for (int i = l; i <= min(r, n); ++i) {
nxt_pos = i;
ins(tr_id[i], val[i], i, l - 1);
// printf("id: %lld, lose_sum: %lld\n", i, lose_sum);
nxt_pos = min(nxt_pos, n);
for (int j = i; j <= nxt_pos; ++j) {
ans[j] = sum - lose_sum;
if (d[R][1] == 1) {
if (tr[1].winner && tr[1].w_val < R) {
ans[j] = lst;
}
if (!tr[1].winner) {
ans[j] += lst;
}
}
}
i = nxt_pos;
}
lst = ans[r];
}
signed main() {
n = read();
m = read();
for (int i = 1; i <= n; ++i) {
a[i] = read();
}
for (int i = 1; i <= m; ++i) {
q[i] = read();
}
K = (int) ceil(log2(n));
for (int i = 0; i < K; ++i) {
scanf("%s", D[i + 1]);
}
for (int i = 1; i <= K; ++i) {
int len = strlen(D[i]);
for (int j = 0; j < len; ++j) {
d[i][j + 1] = D[i][j] - '0';
}
}
int T = read();
while (T--) {
for (int i = 0; i < 4; ++i) {
X[i] = read();
}
pre();
ans[1] = 1;
lst = 1;
for (int i = 1; i <= K; ++i) {
solve((1 << (i - 1)) + 1, 1 << i, i);
}
// printf("ans: ");
// for (int i = 1; i <= n; ++i) {
// printf("%lld ", ans[i]);
// }
// puts("");
int final_ans = 0;
for (int i = 1; i <= m; ++i) {
final_ans = final_ans ^ (i * ans[q[i]]);
}
printf("%lld\n", final_ans);
}
return 0;
}