[COCI2019-2020#4] Klasika 题解

题目传送门

题目分析

约定树节点数量为 \(n\)

由于在线做法维护信息较为困难,因此使用离线做法,先将树建出来再依次处理操作。

考虑如何维护两点间的异或路径和。

比较套路的方法是维护每个点 \(i\) 到根的异或路径和 \(dis_i\),那么点 \(a\) 到点 \(b\) 的异或路径和即为 \(dis_a \operatorname{xor} dis_b\)

这样,Query 操作就变为询问 \(\max \{ dis[a] \operatorname{xor} dis[c] ,c\in \text{b子树} \}\)。但是如果暴力遍历子树,单次的查询复杂度就为 \(O(n)\),难以通过此题。

如果使用 dfs 序,询问就变成了区间查询异或最大值,这可以通过 01Trie 树解决,单次查询时间复杂度降为 \(O(31)\),其中 \(31\) 是数字二进制下的最长长度。

然而肯定不可以对于每个区间都建立一颗 01Trie 树,现在的问题即为如何维护 01Trie 树下标。

我会树套树! 树套树虽然时间复杂度较小,但空间开不下,所以考虑用分块维护 01Trie 树。

对于每个块,建立一颗 01Trie 树。对于 Add 操作,更新当前块的 01Trie 树,复杂度为 \(O(31)\)。对于 Query 操作,散块直接暴力,整块则在 01Trie 树上进行查询。设块大小为 \(S\),单次查询时间复杂度即为 \(O(S+ \dfrac{31n}{S} )\),当 \(S = \sqrt{31n}\) 时时间复杂度最优。

时间复杂度为 \(O(Q\sqrt{31n})\)

#include<bits/stdc++.h>
#define M 200005
typedef long long ll;
using namespace std;
bool f2;
char IO;
int rd(){
	int num=0;bool f=0;
	while(IO=getchar(),IO<48||IO>57)if(IO=='-')f=1;
	do num=(num<<1)+(num<<3)+(IO^48);
	while(IO=getchar(),IO>=48&&IO<=57);
	return f?-num:num;
}
int n,m,Q,S;
int opt[M],X[M],Y[M];
int to[M],hd[M],val[M],nxt[M],cnte;
void Adde(int u,int v,int w){
	to[++cnte]=v;val[cnte]=w;
	nxt[cnte]=hd[u];hd[u]=cnte;
}
int L[M],R[M],dis[M],idfn[M],ntot;
void dfs(int x){
	L[x]=++ntot;idfn[ntot]=x;
	for(int i=hd[x],y;i;i=nxt[i]){
		y=to[i];
		dis[y]=dis[x]^val[i];
		dfs(y);
	}R[x]=ntot;
}
// 01Trie树
int Rt[505],trie[M*32][2],cnt[M*32],tot;
void update(int &rt,int x,int dep=30){
	if(!rt)rt=++tot;
	++cnt[rt];
	if(dep==-1)return ;
	update(trie[rt][(x>>dep)&1],x,dep-1);
}
int query(int rt,int x,int dep=30){
	if(dep==-1||!cnt[rt])return 0;
	int bit=(x>>dep)&1;
	if(cnt[trie[rt][!bit]])
		return query(trie[rt][!bit],x,dep-1)|(1<<dep);
	else return query(trie[rt][bit],x,dep-1);
}
bool Upd[M];
void Update(int x){
	Upd[L[x]]=1;
	update(Rt[L[x]/S],dis[x]);
}
void Query(int L,int R,int x){
	int res=0,kl=L/S,kr=R/S;
	if(kl==kr){
		for(int i=L;i<=R;++i)
			if(Upd[i])res=max(res,dis[idfn[i]]^x);
	}else{
		for(int i=L;i<(kl+1)*S;++i)
			if(Upd[i])res=max(res,dis[idfn[i]]^x);
		for(int i=kl+1;i<kr;++i)
			res=max(res,query(Rt[i],x));
		for(int i=kr*S;i<=R;++i)
			if(Upd[i])res=max(res,dis[idfn[i]]^x);
	}printf("%d\n",res);
}
bool f1;
int main(){
//	cout<<(&f1-&f2)/1024.0/1024.0<<endl;
	Q=rd();n=1;
	char str[8];
	for(int i=1;i<=Q;++i){
		scanf("%s",str);
		opt[i]=(str[0]=='A');
		X[i]=rd(),Y[i]=rd();
		if(opt[i])Adde(X[i],++n,Y[i]);
	}
	dfs(1);S=sqrt(31*n);
	Update(m=1);
	for(int i=1,x;i<=Q;++i){
		if(!opt[i])Query(L[Y[i]],R[Y[i]],dis[X[i]]);
		else Update(++m);
	}
	return 0;
}

posted @ 2021-06-07 08:24  Alnorie  阅读(170)  评论(0)    收藏  举报