题解 弋或树

题目描述

题目链接

题解

写在前面

弋(yì)或树 异或树

前置知识

为了解决本题,你应该要会

  • Trie树模板
  • Trie树二进制表示数

思路

解法一

我会暴力!
最坏情况下时间复杂度为\(O(nm)\)
得分:\(29\)
好水啊

解法water

我会Trie树!
预处理所有子树异或和,加入Trie树,直接二进制查找。
得分:\(0\)
这件事情告诉我们暴力比乱搞好用

解法二

我会修改Trie树模板!

观察本题后发现本题难点在于如何\(O(logx)\)求出以u为根的子树下某一个子树的异或和

思考后发现可以选择在Trie树内记录 Trie树每个节点 所对应的 题目给出的树的节点,再lca\(O(logn)\)查询是否有祖先关系即可

然而当所有子树异或和相同时 Trie树每个节点 所对应的 题目给出的树的节点 可以达到\(n\)个,总的时间复杂度为\(O(m \times logx \times n \times logn)\)
得分:\(29\)

解法三

我会建多个Trie树!
即对于每个节点都建一个Trie树
然而当树退化为链时,建树时间复杂度可以达到\(O(logxn^2)\),空间复杂度为\(O(n^2)\)
得分:\(0\)

解法四

我会离线处理!

对于解法三,我们发现父节点的Trie树其实是由子节点的Trie树决定的

所以说可以考虑先求出叶子节点,再一层一层将Trie树上移
即先求出儿子节点的Trie树,处理完儿子节点的询问,再将其中儿子节点的Trie树拓展到其父亲的Trie树(加入其它子节点即父节点),重复此步骤

这样,当询问的点要么基本覆盖整棵树时,拓展的效率会比较高,当重复较多时,多次处理效率也较高,不过最坏时间复杂度似乎有点难估算

具体算法实现步骤为
(以下将有询问的节点统称为询问节点)

1.预处理
2.找到询问节点u
3.对于每个询问节点u,先找到询问节点u的子树中 子树节点数量最多的询问子节点v
4.并将询问节点u的子树中其他节点加入询问节点v的Trie树中
5.将询问节点u的询问全部处理

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
int n,m,a[100005],x[100005],s[100005],d[100005];
struct que {
	int x,id;
} ans[100005];
vector<que>q[100005];
vector<int>g[100005];
void dfs(int u,int fa) {//步骤1
	s[u]=1,x[u]=a[u];
	for(int i=0; i<g[u].size(); i++) {
		int v=g[u][i];
		if(v==fa)continue;
		dfs(v,u);
		s[u]=s[u]+s[v],x[u]=x[u]^x[v];
	}
}
struct trie {
	vector<int*>c;
	int tot;
	void build() {
		tot=0;
		c.clear(),c.push_back(NULL);
	}
	void insert(int x) {
		int p=0;
		for(int i=30; i>=0; i--) {
			int v=((x&(1<<i))>>i);
			if(c[p]==NULL) {
				c[p]=new int[2];
				memset(c[p],-1,sizeof(int)*2);
			}
			if(c[p][v]==-1) {
				c[p][v]=++tot;
				c.push_back(NULL);
			}
			p=c[p][v];
		}
	}
	int find(int x) {
		int p=0;
		for(int i=30; i>=0; i--) {
			int v=((x&(1<<i))>>i);
			if(c[p][v^1]==-1)p=c[p][v];
			else p=c[p][v^1],x=(x^(1<<i));
		}
		return x;
	}
} tr[100005];
int b,t,y;
void xun(int u,int fa) {//步骤3
	if(q[u].size()>0) {
		if(s[u]>s[b])b=u;
		return;
	}
	for(int i=0; i<g[u].size(); i++) {
		int v=g[u][i];
		if(v==fa)continue;
		xun(v,u);
	}
}
void zhao(int u,int fa) {//步骤4
	if(u==b)return;
	tr[y].insert(x[u]);
	for(int i=0; i<g[u].size(); i++) {
		int v=g[u][i];
		if(v==fa)continue;
		zhao(v,u);
	}
}
void check(int u,int fa) {//步骤2
	for(int i=0; i<g[u].size(); i++) {
		int v=g[u][i];
		if(v==fa)continue;
		check(v,u);
	}
	if(q[u].size()>0) {
		b=0;//步骤3
		for(int i=0; i<g[u].size(); i++) {
			int v=g[u][i];
			if(v==fa)continue;
			xun(v,u);
		}
		if(b==0)d[u]=t++,tr[d[u]].build();
		else d[u]=d[b];
		y=d[u];
		zhao(u,fa);//步骤4
	}
	for(int i=0; i<q[u].size(); i++) {//步骤5
		ans[m].id=q[u][i].id;
		ans[m--].x=q[u][i].x^tr[d[u]].find(q[u][i].x);
	}
}
bool cmp(que a1,que b1) {
	return a1.id<b1.id;
}
inline int read() {
	int s=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9') {
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s;
}
int main() {
	n=read(),m=read();
	for(int i=1; i<=n; i++)a[i]=read();
	for(int i=1; i<n; i++) {
		int u=read(),v=read();
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for(int i=1; i<=m; i++) {
		int u=read(),x=read();
		q[u].push_back((que){x,i});
	}
	dfs(1,0);
	check(1,-1);
	sort(ans+1,ans+1+n,cmp);
	for(int i=1; i<=n; i++)printf("%d\n",ans[i].x);
	return 0;
}
posted @ 2020-02-09 20:33  ezlmr  阅读(338)  评论(0编辑  收藏  举报