题解 弋或树
题目描述
题解
写在前面
弋(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;
}