【TJOI2018】【树链剖分】【可持久化trie】异或

【描述】
现在有一颗以 1为根节点的由 n个节点组成的树,树上每个节点上都有一个权值 vi 。现在有 Q 次操作,操作如下:
1 x y :查询节点 x 的子树中与 y 异或结果的最大值。
2 x y z :查询路径 x 到 y 上点与 z 异或结果最大值
【输入】
第一行是两个数字 n , Q 。
第二行是 n 个数字用空格隔开,第 i 个数字 vi表示点 i 上的权值。
接下来 n−1 行,每行两个数, x,y ,表示节点 x 与 y 之间有边。
接下来 Q 行,每一行为一个查询,格式如上所述。

【思路】

我当时做这道题的时候并不知道树上路径查询可持久化trie或者主席树可以直接通过 s i z u + s i z v − s i z l c a ( u , v ) − s i z f a [ l c a ( u , v ) ] siz_u+siz_v-siz_{lca(u,v)}-siz_{fa[lca(u,v)]} sizu+sizvsizlca(u,v)sizfa[lca(u,v)]实现。我直接无脑上的树剖+可持久化trie,唯一的安慰是跑得挺快的。貌似也没什么好讲的,平时用线段树维护剖分后的链,改成可持久化trie或者主席树就行了。至于查询最大值,可以在可持久化trie或者主席树上按位贪心。
代码:

#include<bits/stdc++.h>
#define re register
#define LL long long
using namespace std;
int n,m,b,c,k;
const int N=2e5+5;
inline int red()
{
    int data=0;int w=1; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return w==1?data:-data;
}
int siz[N],top[N],fa[N],dep[N],val[N],son[N],f[N],nxp[N],cnt=0,rev[N];
namespace trie
{
	int ch[N<<5|1][2],val[N<<5|1],rt[N]={1},cnt=1,tot=0;bool nxt;
	inline int insert(int now)
	{
		int v=rt[tot++];rt[tot]=++cnt;
		int u=cnt;val[u]=val[v]+1;
		for(int re i=30;~i;--i)
		{
			nxt=now&(1<<i);
			ch[u][nxt^1]=ch[v][nxt^1];
			ch[u][nxt]=++cnt;
			u=cnt;v=ch[v][nxt];
			val[u]=val[v]+1;
		}
		return tot;
	}
	inline int query(int now,int u,int v)
	{
		int ret=0;u=rt[u],v=rt[v-1];
		for(int re i=30;~i;--i)
		{
			nxt=now&(1<<i);
			if(val[ch[u][nxt^1]]!=val[ch[v][nxt^1]])u=ch[u][nxt^1],v=ch[v][nxt^1],ret+=(1<<i);
			else u=ch[u][nxt],v=ch[v][nxt];
		}
		return ret;
	}
}
struct node{int u,v;}e[N];
inline void add(int u,int v){e[++cnt].u=u;e[cnt].v=v;nxp[cnt]=f[u];f[u]=cnt;}
void dfs1(int u)
{
	siz[u]=1;
	for(int re i=f[u];i;i=nxp[i])
	{
		int v=e[i].v;
		if(dep[v])continue;
		dep[v]=dep[u]+1;fa[v]=u;
		dfs1(v);siz[u]+=siz[v];
		if(siz[son[u]]<siz[v])son[u]=v;
	}
}
void dfs2(int u)
{
	if(son[u])
	{
		top[son[u]]=top[u];
		rev[son[u]]=trie::insert(val[son[u]]);
		dfs2(son[u]);
	}
	for(int re i=f[u];i;i=nxp[i])
	{
		int v=e[i].v;
		if(top[v])continue;top[v]=v;
		rev[v]=trie::insert(val[v]);
		dfs2(v);
	}
}
inline int path(int x,int y,int z)
{
	int fx=top[x],fy=top[y];
	int mx=0;
	while(fx!=fy)
	{
		if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
		mx=max(mx,trie::query(z,rev[x],rev[top[x]]));
		x=fa[fx],fx=top[x];
	}
	if(dep[x]<dep[y])swap(x,y),swap(fx,fy);
	mx=max(mx,trie::query(z,rev[x],rev[y]));
	return mx;
}
int main()
{
	n=red();m=red();
	for(int re i=1;i<=n;i++)val[i]=red();
	for(int re i=1;i^n;i++)b=red(),c=red(),add(b,c),add(c,b);
	dep[1]=1;top[1]=1;rev[1]=trie::insert(val[1]);
	dfs1(1);dfs2(1);
	for(int re i=1;i<=m;i++)
	{
		int op=red();b=red(),c=red();
		if(op==1)printf("%d\n",trie::query(c,rev[b]+siz[b]-1,rev[b]));
		if(op==2)printf("%d\n",path(b,c,red())); 
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值