西瓜树
作者:互联网
题面
给定一棵 \(N\) 个节点的西瓜树,第 \(i\) 个点有一个权值 。
对每个点 \(i\) ,其答案为不在以它为根的子树中(子树包括它自己)的所有点中,选两个点异或后的最大值,如果选不出两个点,则认为的答案是0。
求每个点的答案。
\(N\le 5\times 10^5\)
解法
一道01tire好题(写起来其实也不难)。
首先会发现样例输出里有许多一样的答案,原因是假如树中最大的异或值由x和y贡献,那么凡是子树内不包含x且不包含y的节点的答案都会是这个最大值。
这个最大值可以 \(O(N)\) 求得,用01字典树即可。剩下的问题就是考虑有哪些节点的子树内包含x和y。很简单,在x到根的路径上和y到根的路径上的所有点子树内都包含它们。对于这些节点直接硬求即可,考虑从根往下走走到x或y为止,每次向下走一步,就暴力扩展出新的可以选择的点,然后还是上01trie求解。
这个部分也是 \(O(N)\) 的,当然01tire自带的那个常数嘛……
#include<cstdio>
#include<vector>
#include<cstring>
#define zczc
#define ll long long
using namespace std;
const int N=500010;
inline ll read(){
ll wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
wh*=f;return wh;
}
int m,f[N];
vector<int>son[N];
ll a[N],ans[N];
namespace trie{
struct node{
int next[2];
}newone;
vector<node>ve;
ll nans;
void clear(){
nans=0;
ve.clear();
ve.push_back(newone);
return;
}
void push(ll wh){
bool c[61];
for(int i=0;i<=60;i++)c[i]=(wh&(1ll<<(60-i)))>0;
int now=0;
for(int i=0;i<=60;i++){
if(ve[now].next[c[i]]==0)ve[now].next[c[i]]=ve.size(),ve.push_back(newone);
now=ve[now].next[c[i]];
}
ll an=0;now=0;
for(int i=0;i<=60;i++){
an<<=1;
if(ve[now].next[1-c[i]]){an+=1-c[i];now=ve[now].next[1-c[i]];}
else{an+=c[i];now=ve[now].next[c[i]];}
}
if((wh^an)>nans)nans=(wh^an);
return;
}
ll ask(ll wh){
bool c[61];
for(int i=0;i<=60;i++)c[i]=(wh&(1ll<<(60-i)))>0;
int now=0;ll an=0;
for(int i=0;i<=60;i++){
an<<=1;
if(ve[now].next[1-c[i]]){
an+=1-c[i];
now=ve[now].next[1-c[i]];
}
else{
an+=c[i];
now=ve[now].next[c[i]];
}
}
return an;
}
}
void npush(int wh){
trie::push(a[wh]);
//for(int data:son[wh]){
//npush(data);
//}
for(vector<int>::iterator it=son[wh].begin();it!=son[wh].end();it++){
npush(*it);
}
return;
}
void dfs(int wh,int th){
if(wh==0)return;
dfs(f[wh],wh);
ans[wh]=trie::nans;
trie::push(a[wh]);
//for(int data:son[wh]){
//if(data^th)npush(data);
//}
for(vector<int>::iterator it=son[wh].begin();it!=son[wh].end();it++){
if((*it)^th)npush(*it);
}
return;
}
void solve(int wh){
trie::clear();
dfs(wh,0);
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
memset(ans,-1,sizeof(ans));
m=read();
for(int i=2;i<=m;i++){
f[i]=read();
son[f[i]].push_back(i);
}
for(int i=1;i<=m;i++)a[i]=read();
ll x,y,an=0;int px,py;
trie::clear();
for(int i=1;i<=m;i++){
trie::push(a[i]);
ll nan=trie::ask(a[i]);
if((nan^a[i])>an){
an=nan^a[i];
y=a[i],x=nan;
}
}
for(int i=1;i<=m;i++){
if(a[i]==x)px=i;
else if(a[i]==y)py=i;
}
solve(px);
solve(py);
for(int i=1;i<=m;i++)printf("%lld\n",ans[i]>=0?ans[i]:an);
return 0;
}
update 艹机房不支持C++11,用不了for(data:vector)
的写法。
标签:西瓜,return,int,ll,son,ans,wh 来源: https://www.cnblogs.com/dai-se-can-tian/p/15484298.html