其他分享
首页 > 其他分享> > 西瓜树

西瓜树

作者:互联网

题面

给定一棵 \(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