[20190925机房测试] 不正常国家

有一个长相惊奇的国家....
可以猜到,这个国家有N个城市,每个城市之间有且仅有一条通路
可以猜到,这个国家是长在树上的
可以猜到,首都是1号节点
可以猜到,每个城市管辖他子树中的所有城市
可以猜到,每个城市有一个权值A;,两个城市通讯难度为两城市路径异或和
可以猜到,一个城市的繁忙度定义为它所管辖的城市中通讯难度最大的两个城市的通讯难
度
可以猜到, 如果两个城市(x,y)都是在a的子树中,但是lca(x.y)!=a,那么这两个城市不参与a繁忙度的统计
可以猜到,这道题你只需要输出所有城市的繁忙度即可
可以猜到,这道题是水题....

一句话题意:对于每个点 (i),询问所有路径 ((x,y)) 中的最大值
其中,对于路径 ((x,y))(lca(x,y)=i)

这题可真是啊……打暴力都差点打炸

暴力的思路很简单
预处理出LCA和异或前缀和, $Theta (n^2) $ 枚举所有边
每次更新(ans_i)(以 (i) 为lca的路径的异或最大值)
最后输出每个点的答案即可

暴力代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int n,a[100005],u,v;
int Xor[100005];

struct Edge
{
	int next,to;
}edge[400005];
int cnt=0,head[200005];

int ans[100005];

inline void add_edge(int from,int to)
{
	edge[++cnt].next=head[from];
	edge[cnt].to=to;
	head[from]=cnt;
}

int anc[200005][25],dep[200005];
void dfs1(int u,int fa)
{
	anc[u][0]=fa;
	for(register int i=1;i<=20;++i)
		anc[u][i]=anc[anc[u][i-1]][i-1];
	for(int i=head[u];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(v==fa) continue;
		dep[v]=dep[u]+1;
		dfs1(v,u);
	}
}

void dfs2(int next,int fa,int cnt)
{
	for(register int i=head[next];i;i=edge[i].next)
	{
		if(edge[i].to!=fa)
		{
			if(cnt==0) Xor[edge[i].to]=a[i];
			Xor[edge[i].to]=Xor[next]^a[edge[i].to];
			dfs2(edge[i].to,next,cnt+1);
		}
	}
}

int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	int deltaDep=dep[x]-dep[y];
	for(register int i=0;deltaDep;deltaDep>>=1,++i)
		if(deltaDep&1) x=anc[x][i];
	if(x==y) return x;
	for(register int i=20;anc[x][0]!=anc[y][0];--i)
	if(anc[x][i]!=anc[y][i])
	{
		x=anc[x][i];
		y=anc[y][i];
	}
	return anc[x][0];
}

template<class T>inline void read(T &res)
{
	char c;T flag=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
	while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}

int main()
{
	freopen("irregular.in","r",stdin);
	freopen("irregular.out","w",stdout);
	read(n);
	for(register int i=1;i<=n;++i) read(a[i]),Xor[i]=a[i];
	for(register int i=2;i<=n;++i)
	{
		read(u);read(v);
		add_edge(u,v);
		add_edge(v,u);
	}
	dfs1(1,0);
	dfs2(1,0,0);
	for(register int i=1;i<=n;++i)
	{
		for(register int j=i;j<=n;++j)
		{
			if(i==j)
			{
				if(a[i]>ans[i]) ans[i]=a[i];
				continue;
			}
			int LCA=lca(i,j);
			int kkk=(Xor[i]^Xor[j]^a[LCA]);
			if(kkk>ans[LCA]) ans[LCA]=kkk;
		}
	}
	for(register int i=1;i<=n;++i) printf("%d ",ans[i]);
	return 0;
}

(Theta (n^2)) 的枚举,我们显然是接受不了的
于是我们需要搞一棵trie树,直接启发式合并

正解:

#include<bits/stdc++.h>
#define N 100005
using namespace std;

int n,x,y,a[N],ans[N];
int size[N],son[N],w[N];
int ndsum,root[N],nxt[N*31][2],temp=30;

struct Edge
{
	int next,to;
}edge[N<<1];
int cnt=1,head[N];

void add_edge(int from,int to)
{
	edge[++cnt].next=head[from];
	edge[cnt].to=to;
	head[from]=cnt;
}

template<class T>inline void read(T &res)
{
	char c;T flag=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
	while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}

void df1(int u,int fa)
{
	w[u]=w[fa]^a[u];
	size[u]=1;
	for(int i=head[u];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(v==fa) continue;
		df1(v,u);
		size[u]+=size[v];
		if(size[son[u]]<size[v]) son[u]=v;
	}
}

int ask(int u,int x)
{
	int now=root[u],ret=0;
	for(int i=temp;i>=0;--i)
	{
		int c=(x>>i & 1);
		if(nxt[now][!c])
		{
			ret+=(1<<i);
			now=nxt[now][!c];
		}
		else now=nxt[now][c];
	}
	return ret;
}

void ins(int u,int x)
{
	if(!root[u]) root[u]=++ndsum;
	int now=root[u];
	for(int i=temp;i>=0;--i)
	{
		int c=(x>>i & 1);
		if(!nxt[now][c]) nxt[now][c]=++ndsum;
		now=nxt[now][c];
	}
}

void ii(int u,int fa,int rot)
{
	ins(rot,w[u]);
	for(int i=head[u];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(v==fa) continue;
		ii(v,u,rot);
	}
}

void qq(int u,int fa,int rot)
{
	ans[rot]=max(ans[rot],ask(rot,w[u]^a[rot]));
	for(int i=head[u];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(v==fa) continue;
		qq(v,u,rot);
	}
}

void dfs2(int u,int fa)
{
	if(son[u]) dfs2(son[u],u),root[u]=root[son[u]];//先处理重儿子 
	ans[u]=max(ans[u],ask(u,w[u]^a[u]));
	ins(u,w[u]);
	for(int i=head[u];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(v==fa||v==son[u]) continue;
		int now=ndsum;
		dfs2(v,u);//处理轻儿子 
		for(int j=now+1;j<=ndsum;++j) nxt[j][0]=nxt[j][1]=0;//还原 
		ndsum=now;
		qq(v,u,u);
		ii(v,u,u);
	}
}

int main()
{
	freopen("irregular.in","r",stdin);
	freopen("irregular.out","w",stdout);
	read(n);
	for(int i=1;i<=n;++i) read(a[i]),ans[i]=a[i];
	for(int i=1;i<n;++i)
	{
		read(x);read(y);
		add_edge(x,y);
		add_edge(y,x);
	}
	df1(1,0);
	dfs2(1,0);
	for(int i=1;i<=n;++i) printf("%d ",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/tqr06/p/11586988.html