其他分享
首页 > 其他分享> > ARC137-D 报灵智慧壬

ARC137-D 报灵智慧壬

作者:互联网

l1nk

editorial


给定序列 \(\{a_n\}\) ,求出 \(a_n\) 在 \(k \in [1,m]\) 次前缀异或和后的值.

一个显然的事情是我会且仅会打暴力(?

赛后 \(\text{dottle}\) 过来讲题
发现它又考了一次 \(\zeta\) 变换

草, 我果然还是不会做

因为异或和加法非常像所以我们把它当成加法处理
那么就是快速求得 \(a_i\) 在 \(k\) 次前缀和中对 \(a_n\) 的贡献

这个问题实质上等价于
若在位置 \(i\) 时可以走到 \([i,n]\) 的任意位置,要用 \(k\) 步从 \(i\) 走到 \(n\) 的所有路径

这个问题实质上也就是
有 \(m\) 个无标号球,放进 \(n\) 个盒子的方案数

为了便于插板每个盒子先放一个
那么答案就是 \(\dbinom{n+m-1}{n-1}\)
所以对于 \(a_i\) ,当 \(f_i=\dbinom{n-i+k}{n-i} \mod 2 =1\) 时,对 \(a_n\) 有一次贡献

然后有一个性质:
若 \(\dbinom{n}{m} \mod 2=1\) ,当且仅当 \(n\&m=m\)

在网上搜到了一个证明,orz
但是他的 \(\LaTeX\) 好像炸了(
所以我再写一遍

证明:

\[\dbinom{n}{m}=\frac{n!}{m!(n-m)!} \]

设 \(n!\) , \(m!\) , \((n-m)!\) 分解质因数后 \(2\) 的个数为 \(A\) , \(B\) , \(C\)
显然若 \(\dbinom{n}{m} \mod 2=1\) 那么一定有 \(A=B+C\)
又有性质:

\[f(p,n)=\sum_{i=1}^{\infty} \lfloor \frac{n}{p^i} \rfloor \]

其中 \(f(p,n)\) 表示 \(n!\) 质因数分解中 \(p\) 的个数, \(p \in Prime\)
结合 \(n\) 的二进制表示

\[bit(n)=\sum_{i=1}^{k}a_i\times2^{i-1} \]

那么

\[f(2,n)=\sum_{i=1}^{k}\lfloor \frac{\sum_{j=1}^{k}a_j\times2^{j-1}}{2^i} \rfloor \]

其中 \(k\) 为 \(bit(n)\) 的位数
所以

\[f(2,n)=\sum_{i=1}^{k}\frac{\sum_{j=i+1}^{k}a_j\times2^{j-1}}{2^i} \]

(关于这一步的舍入在后文有证明)
写得更好看就是

\[f(2,n)=\sum_{i=1}^{k}\sum_{j=i+1}^{k}a_j\times2^{j-i-1} \]

\[=\sum_{j=1}^{k}a_j\sum_{i=0}^{j-2}2^i \]

\[=\sum_{j=1}^{k}a_j(2^{j-1}-1) \]

\[=(\sum_{j=1}^{k}a_j\times2^{j-1})-(\sum_{j=1}^{k}a_j) \]

\[=n-\sum_{j=1}^{k}a_j \]

\[=n-\text{popcount}(n) \]

\[f(2,n)=n-\text{popcount}(n) \]

再设 \(n\) , \(m\) , \((n-m)\) 的 \(\text{popcount}\) 为 \(a\) , \(b\) , \(c\)
那么

\[n-a=(m-b)+(n-m-c) \]

\[n-a=n-(b+c) \]

也就是说

\[a=b+c \]

观察可以发现当且仅当 \(bit(m) \subseteq bit(n)\)
也就是说 \(n\&m=m\)

\(\text{Q.E.D.}\)

证明了这个性质后,我们把贡献还原回异或,发现一个 \(a_i\) 在第 \(k\) 轮对 \(a_n\) 产生一次 \(\text{XOR}\) 贡献当且仅当

\[\dbinom{n-i+k}{n-i}\mod2=1 \]

那么就有

\[(n-i+k)\&(n-i)=(n-i) \]

因为这个柿子又等价于

\[k\&(n-i)=0 \]

所以

\[\complement_{u}(k)\&(n-i)=(n-i) \]

据此, \(\text{dottle}\) 使用了总复杂度为 \(O(m3^{k})\) 枚举 \(\complement_{u}(k)=Mx-k\) 的子集的写法:

for(ri k=0;k<=m-1;++k){
    ri ans=a[n];
    for(ri id=Mx-k;id;id=(id-1)&(Mx-k)) //枚举下标中为Cu(k)子集的部分 
    if(id<=n) ans^=a[n-id];
    writesp(ans);
}

虽然能过,但是获得了 \(3073ms\) 的优异成绩(

看着std的 \(177ms\) 我茫然失措......

然后发现这个东西其实就是对 \(Mx-k\) 求子集函数和

欸那不就 \(\text{Fast Zeta Transform}\) 了吗???

我们对全集 \(Mx\) 做一次 \(\zeta\) 变换.
具体地,我们把 \(zeta[n-i]\) 初值设置为 \(a[i]\)
求一次正常的快速zeta变换
最后在全集中找到每个 \(zeta[Mx-k]\)
此时它们已经被全部计算完毕,所以直接输出即可

ri Mx=1,t=max(n,m),p0wer=0;
while(Mx-1<=t) Mx<<=1,++p0wer;
--Mx,--p0wer;
vector<int> zeta(Mx); 
for(ri j=1;j<=n;++j)
zeta[n-j]=a[j];
for(ri i=0;i<=p0wer;++i)
for(ri bits=0;bits<=Mx;++bits)
if(bits&(1<<i)) zeta[bits]^=zeta[bits^(1<<i)];
for(ri k=0;k<=m-1;++k)
writesp(zeta[Mx-k]);

为什么可以一次计算完毕呢?
因为我们进行全集的zeta变换时,同时也计算了所有子集的zeta变换

那么最终我们的算法复杂度就是 \(O(n+k2^k) \to O(n+MlogM)\) ,其中 \(M=max\{n,m\}\)

最后跑出了 \(114ms\) .


附:对证明过程中去掉下取整步骤的小学生证明

舍掉的部分为

\[\sum_{j=1}^{i}a_j2^{j-1-i} \le \sum_{j=1}^{i}2^{j-1-i}=2^{-i}\sum_{j=1}^{i}2^{j-1} \]

由小学的等比数列知识可知

\[\text{RHS}=2^{-i}\times2^0\times(2^i-1)=1-2^{-i} \lt 1 \]

所以它们和下取整一起去掉是正确的

\(\text{Q.E.D.}\) (雾

标签:ARC137,times2,dbinom,text,sum,智慧,报灵,zeta,Mx
来源: https://www.cnblogs.com/suitlie/p/16210754.html