博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

EOJ Monthly 2020.9.F.动态树(线段树套Trie/树状数组套Trie/分块+Trie)

题目链接


\(Description\)
维护一棵树,有两种操作:
1.Add x,y,插入一个节点,父节点为\(x\)边权为\(y\)
2.Query x,y,查询起点为\(x\),终点在\(y\)的子树中的最大简单路径边权异或和。

\(Solution\)
Sol 1
先离线求出树的DFS序,以及\(val[x]\)表示\(1\)号点到\(x\)的路径权值异或和,每次查询即求一个\(y\)子树中与\(val[x]\)异或最大的\(val[z]\)
\(y\)子树即一个区间,用线段树套Trie就ok了。
每次\(Add\)\(val[node]\)插入包含它的所有区间的Trie,每次\(Query\)求一下区间中与\(val[x]\)异或的最大值。
复杂度\(O(q\log q\log v)\)

Sol 2
依旧离线先求DFS序。注意到 查询其实就是序列上一个区间的查询,在可持久化Trie中用\(Trie[r]-Trie[l-1]\)\(size\)就可以解决。
那么修改就应该是前缀的插入Trie,用树状数组套Trie就ok了。查询时\(r\)\(l-1\)在树状数组上的所有节点都需要考虑。
复杂度\(O(q\log q\log v)\),常数比线段树小很多。(tql)

Sol 3
\(Sol 2\),注意到 查询其实就是序列上一个区间的查询,我们可以分块。
对每个块建一棵Trie,插入时插入对应块的Trie。查询时对整块查Trie,零散部分直接\(O(\sqrt n)\)\(O(1)\)查询。
设块大小为\(s\)\(Add\)次数为\(n\)\(Query\)次数为\(m\),复杂度\(O(n\log v+m(\frac ns\log v+2s))=O(n\log v+m\sqrt(n\log v))\),常数非常非常小。(tttql)

ps:
注意1号节点要插入权值0。


线段树套Trie:

//1.554s	650.645MB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define BIT 30
#define gc() getchar()
//#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=2e5+5;

int cnt,Enum,H[N],nxt[N],val[N],L[N],R[N];
//char IN[MAXIN],*SS=IN,*TT=IN;
struct Queries
{
	int opt,x,y;
}q[N];
struct TRIE
{
	#define S N*19*31
	int tot,son[S][2];
	#undef S
	void Insert(int x,int v)
	{
		for(int i=BIT; ~i; --i)
		{
			int c=v>>i&1;
			if(!son[x][c]) son[x][c]=++tot;
			x=son[x][c];
		}
	}
	int Query(int x,int v)
	{
		if(!x) return 0;
		int res=0;
		for(int i=BIT; ~i; --i)
		{
			int c=(v>>i&1)^1;
			son[x][c]?res|=1<<i:c^=1;
			x=son[x][c];
		}
		return res;
	}
}Trie;
struct Segment_Tree
{
	#define S N*19
	int root[S];
	#undef S
	#define ls rt<<1
	#define rs rt<<1|1
	#define lson l,m,ls
	#define rson m+1,r,rs
	void Modify(int l,int r,int rt,int L,int R,int v)
	{
		if(!root[rt]) root[rt]=++Trie.tot;
		Trie.Insert(root[rt],v);
		if(L<=l && r<=R) return;
		int m=l+r>>1;
		if(L<=m) Modify(lson,L,R,v);
		if(m<R) Modify(rson,L,R,v);
	}
	int Query(int l,int r,int rt,int L,int R,int v)
	{
		if(L<=l && r<=R) return Trie.Query(root[rt],v);
		int m=l+r>>1;
		if(L<=m)
			if(m<R) return std::max(Query(lson,L,R,v),Query(rson,L,R,v));
			else return Query(lson,L,R,v);
		return Query(rson,L,R,v);
	}
}T;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now;
}
inline bool GetOpt()
{
	char c=gc();
	while(!isalpha(c)) c=gc();
	return c=='Q';
}
inline void AE(int u,int v)
{
	nxt[v]=H[u], H[u]=v;
}
void DFS(int x)
{
	static int Index=0;
	L[x]=++Index;
	for(int v=H[x]; v; v=nxt[v]) DFS(v);
	R[x]=Index;
}

int main()
{
	int Q=read(); cnt=1;
	for(int i=1,x; i<=Q; ++i)
		switch(GetOpt())
		{
			case 0: x=read(),val[++cnt]=val[x]^read(),AE(x,cnt),q[i]=(Queries){0,x,cnt}; break;
			case 1: x=read(),q[i]=(Queries){1,x,read()}; break;
		}
	DFS(1);

#define S 1,cnt,1
	T.Modify(S,1,1,0);
	for(int i=1; i<=Q; ++i)
		switch(q[i].opt)
		{
			case 0: T.Modify(S,L[q[i].y],L[q[i].y],val[q[i].y]); break;
			case 1: printf("%d\n",T.Query(S,L[q[i].y],R[q[i].y],val[q[i].x])); break;
		}

	return 0;
}

树状数组套Trie:

//1.162s	486.703MB
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#define BIT 30
#define gc() getchar()
//#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=2e5+5;

int cnt,Enum,H[N],nxt[N],val[N],L[N],R[N];
//char IN[MAXIN],*SS=IN,*TT=IN;
struct Queries
{
	int opt,x,y;
}q[N];
struct TRIE
{
	#define S N*19*31
	int tot,son[S][2],sz[S];
	#undef S
	void Insert(int x,int v)
	{
		++sz[x];
		for(int i=BIT; ~i; --i)
		{
			int c=v>>i&1;
			if(!son[x][c]) son[x][c]=++tot;
			x=son[x][c], ++sz[x];
		}
	}
	int Query(int x,int v)
	{
		if(!x) return 0;
		int res=0;
		for(int i=BIT; ~i; --i)
		{
			int c=(v>>i&1)^1;
			son[x][c]?res|=1<<i:c^=1;
			x=son[x][c];
		}
		return res;
	}
}Trie;
struct Bit
{
	int n,root[N];
	std::vector<int> v1,v2;
	#define lb(x) (x&-(x))
	void Modify(int p,int v)
	{
		for(; p<=n; p+=lb(p))
			!root[p]&&(root[p]=++Trie.tot), Trie.Insert(root[p],v);
	}
	void GetP(int p,std::vector<int> &vec)
	{
		for(; p; p^=lb(p)) vec.push_back(root[p]);
	}
	int Query(int l,int r,int v)
	{
		std::vector<int>().swap(v1), std::vector<int>().swap(v2);
		GetP(l-1,v1), GetP(r,v2);
		int res=0;
		for(int i=BIT; ~i; --i)
		{
			int c=(v>>i&1)^1,s=0;
			for(auto j:v1) s-=Trie.sz[Trie.son[j][c]];
			for(auto j:v2) s+=Trie.sz[Trie.son[j][c]];
			s>0?res|=1<<i:c^=1;
			for(auto &j:v1) j=Trie.son[j][c];
			for(auto &j:v2) j=Trie.son[j][c];
		}
		return res;
	}
}T;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now;
}
inline bool GetOpt()
{
	char c=gc();
	while(!isalpha(c)) c=gc();
	return c=='Q';
}
inline void AE(int u,int v)
{
	nxt[v]=H[u], H[u]=v;
}
void DFS(int x)
{
	static int Index=0;
	L[x]=++Index;
	for(int v=H[x]; v; v=nxt[v]) DFS(v);
	R[x]=Index;
}

int main()
{
	int Q=read(); cnt=1;
	for(int i=1,x; i<=Q; ++i)
		switch(GetOpt())
		{
			case 0: x=read(),val[++cnt]=val[x]^read(),AE(x,cnt),q[i]=(Queries){0,x,cnt}; break;
			case 1: x=read(),q[i]=(Queries){1,x,read()}; break;
		}
	DFS(1);

	T.n=cnt, T.Modify(1,0);
	for(int i=1; i<=Q; ++i)
		switch(q[i].opt)
		{
			case 0: T.Modify(L[q[i].y],val[q[i].y]); break;
			case 1: printf("%d\n",T.Query(L[q[i].y],R[q[i].y],val[q[i].x])); break;
		}

	return 0;
}
posted @ 2020-10-19 10:10  SovietPower  阅读(109)  评论(0编辑  收藏  举报