题解 P6623 【[省选联考 2020 A 卷] 树】
dengyaotriangle · · 题解
首先,我们要求的是一个子树和状物体,那么就可以先考虑一个点,对于它的所有祖先的答案的贡献。
首先考察
随便选一个
那么
我们随便找一位来看,比如说第二低的位:
按顺序串起来是
进一步地观察上方表格其它列,我们发现,第
我们考虑对于每一位分别计算贡献。那么,根据题面,对于每一位,它对它的
而我们要求的是每一个点的异或和,所以说其实对于第
级祖先的答案异或上
上面的那些区间可以很容易地根据我们之前找到的规律求出来,(循环节是
而那个
但怎么维护把上述那么多区间全异或上
级祖先的差分数组上均异或上
但是我们有这么多的区间,树上差分依然是
但是,观察到,所有要异或
大概是这样(好丑)↑
那么,我们发现,我们修改的那很多的差分,就是单独地改了一个
但是怎么找到一个点的差分值呢?我们发现,它的差分值,就等于进入这个点的子树前的
原因见下图。
因为我们一个点的差分,就是其子树内的点对于
所以,我们对于每一个节点的每一位,都可以
上述题解提供的是基础思路,具体的计算和实现可以看代码。
本做法因为只使用了树上差分而没有使用高级数据结构,所以常数很小。
#include<bits/stdc++.h>
using namespace std;
//dengyaotriangle!
const int maxl=21;
const int maxn=1<<maxl;
int n;
unsigned a[maxn];
vector<int> adj[maxn];
unsigned w[maxl][maxn];
unsigned long long tans=0;
unsigned dfs(int u,unsigned d){
unsigned ans=a[u];
for(int j=0;j<maxl;j++)w[j][(d+a[u])&((1u<<j)-1u)]^=1u<<j;
for(int j=0;j<maxl;j++)ans^=w[j][d&((1u<<j)-1u)];
for(int i=0;i<adj[u].size();i++){
int v=adj[u][i];
ans^=dfs(v,d+1);
}
for(int j=0;j<maxl;j++)ans^=w[j][d&((1u<<j)-1u)];
//cerr<<u<<' '<<ans<<endl;
tans+=ans;
return ans;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=2;i<=n;i++){
int f;cin>>f;
adj[f].push_back(i);
}
dfs(1,0);
cout<<tans;
return 0;
}