题解:P8526 [Ynoi2078] 《How to represent part-whole hierarchies in a neural network》阅读报告(更新中...)
我们考虑对
设当前枚举到右端点
我们需要做的操作:
- 对于
i \in [l, r] ,a_i+1 \to a_i 。 - 对于
i \in [1, p] ,b_i \operatorname{xor} a_i\to b_i 。 - 查询
b 的区间异或和。
考虑序列分块。我们先预处理出
对于第一类操作:
- 散块:暴力修改,暴力重构。
- 整块:我们维护一个
tag 数组,每次修改整块时tag[o]++,若tag_o=B ,须重构整块。
对于第二类操作:
- 散块:暴力重构。
- 整块:直接令
sumb_o \operatorname{xor} f_{o, tag_o} \to sumb_o ,同时维护一个桶k ,每次将tag_o 加入k_o ,用于维护b 。
第三类是平凡的。
现在我们只剩下了一个问题:如何暴力重构整块?
我们需要干两件事:重新计算
更新
注意到
这样直接枚举值域并双指针的做法是
我们将
对于第一类
对于第二类
如果你看到这里一脸蒙逼,请结合下面的代码一起食用。
const int B=511;
void rebuild(const int o){
const int l=bl[o], r=br[o];
rep(i, l, r) num[a[i]&B]^=1;//开一个桶
per(k, 8, 0){
int s=1<<k+1, now=0;
rep(i, 1, s-1){
now^=num[s-i];
if(now) pre[k][i]=s;//pre[k][i] 表示在 模 2^k 下,i 的贡献
}
rep(i, 1, s-1) if(i>>k&1) num[i^1<<k]^=num[i], num[i]=0;
}
num[0]=0;
rep(k, 0, 7){
const int s=1<<k+1;
rep(i, 1, s-1){
pre[k+1][i]^=pre[k][i];
pre[k+1][i|s]^=pre[k][i];
//进行前缀和
pre[k][i]=0;
}
}
rep(i, 0, B) f[o][i]=pre[8][i], pre[8][i]=0;//算出第一部分(x<=B)的贡献
rep(i, l, r){
int p=__builtin_ctz((a[i]>>9)^O);
if(p) num[(1<<10)-(a[i]&1023)]^=(1<<10+p)-(1<<10);
/*
第二部分。
此时 x<=B<=k/2
想要让 x+a_i%k>=k,a_i 第10,11,12一直到log2(k)位必须为1
这就是为什么“贡献一定是一个k的前缀”
p即为 a_i 的最长连续1段的长度
*/
}
int now=0;
rep(i, 0, B) now^=num[i], num[i]=0, f[o][i]^=now;
int s=0;
rep(i, l, r) s^=a[i];//计算不进位的贡献
rep(i, 0, B){
f[o][i]^=s;
if(r-l+1&1) f[o][i]^=i;
}
}