Luogu6623 [省选联考 2020 A 卷] 树
Luogu6623 [省选联考 2020 A 卷] 树
不仅没能口胡出算法,还被位运算弄的搞不清了。
树上差分
我们先设权值为\(0\)。
对于每一位\(k\)进行考虑。
那么节点\(u\)对其一级祖先、二级祖先等的贡献分别是\(1,2,\cdots\),在第\(k\)位会贡献\(1\)的区间为\([a\times 2^{k+1}+2^k,(a+1) \times 2^{k+1}) (a \in N)\)。
考虑差分,在所有\(a \times 2^k\)级祖先处\(\operatorname{xor}\)上\(2^k\),即完成了差分操作。
带上自己的深度,也就是满足\(d_v \equiv d_u \bmod{2^k}\)的所有点。
带上权值,可以看成一个深度为\(d_u+val_u\)的点往上更新。
有一个技巧,就是可以利用一个节点遍历子树前的贡献与遍历子树后的贡献进行异或差,就去除了其他位置的贡献。
那么这道题就做完了。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<list>
#define N 525015
#define ui unsigned int
#define ull unsigned long long
using namespace std;
ui n,x,y,a[N];
ui c[21][1 << 20];
ull ans;
list<ui>e[N];
ui dfs(ui u,ui d)
{
ui t=a[u];
for (ui i=0;i<21;++i)
c[i][(a[u]+d) & ((1u << i)-1u)]^=1u << i;
for (ui i=0;i<21;++i)
t^=c[i][d & ((1u << i)-1u)];
for (list<ui> ::iterator it=e[u].begin();it!=e[u].end();++it)
t^=dfs(*it,d+1);
for (ui i=0;i<21;++i)
t^=c[i][d & ((1u << i)-1u)];
ans+=t;
return t;
}
int main()
{
scanf("%u",&n);
for (ui i=1;i<=n;++i)
scanf("%u",&a[i]);
for (ui i=2;i<=n;++i)
scanf("%u",&x),e[x].push_front(i);
dfs(1,0);
printf("%llu\n",ans);
return 0;
}