CF840E In a Trap

一、题目

点此看题

二、解法

感冒在家两天,今天才回学校,虽然博客鸽了一天但是我换签名了

对于询问其实可以分块,每一块的前 \(8\) 位都是一样的,那么处理后 \(8\) 位就可以了,设 \(f(u,i)\) 表示 \(u\) 向上的 \(256\) 个节点中,最大的 \(a_v\oplus (dep_u-dep_v)\oplus (i\cdot 256)\),那么查询可以先跳整块再跳散块。

对于上面这东西显然可以值域分治,对于前 \(8\) 位我们可以搞一个 \(\tt trie\) 树直接查询。对于后 \(8\) 位我们开一个桶 \(g(u,i)\) 表示前 \(8\) 位为 \(i\)\(a_v\),最大的 \(((dep_u-dep_v)\oplus a_v)\and 256\),那么 \(f(u,i)\) 就可以通过这两者拼凑出来。

预处理的复杂度是 \(O(n\log n\sqrt n)\),询问的复杂度是 \(O(q\sqrt n)\)

三、总结

看到 \(a_i\leq n\) 之类的限制多半要用到值域分块,对于加法和异或的混合问题可以考虑对半拆位。

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 50005;
const int N = 256;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,a[M],fa[M],dep[M],f[M][260],mx[M][260];
vector<int> g[M];int cnt,ch[M][2],lst[M];
void upd(int &x,int y) {x=max(x,y);}
void ins(int x)
{
	for(int i=7,p=1;i>=0;i--)
	{
		int w=(x>>i)&1;
		if(!ch[p][w]) ch[p][w]=++cnt;
		p=ch[p][w];
	}
}
int ask(int x,int u)
{
	int v=0,res=0;
	for(int i=7,p=1;i>=0;i--)
	{
		int w=((x>>i)&1)^1;
		if(ch[p][w]) p=ch[p][w],res|=(1<<i);
		else p=ch[p][w^=1];
		v|=(w<<i);
	}
	return (res<<8)|mx[u][v];
}
void dfs(int u)
{
	if(dep[u]>=N)
	{
		for(int i=1;i<=cnt;i++) ch[i][0]=ch[i][1]=0;
		cnt=1;int i=u;
		for(;dep[u]-dep[i]<N;i=fa[i])
		{
			upd(mx[u][a[i]>>8],(dep[u]-dep[i]^a[i])&255);
			ins(a[i]>>8);
		}
		lst[u]=i;
		for(i=0;i<N;i++) f[u][i]=ask(i,u);
	}
	for(auto v:g[u]) if(v^fa[u])
		fa[v]=u,dep[v]=dep[u]+1,dfs(v);
}
signed 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);
	}
	dep[1]=1;dfs(1);
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),ans=0,d=0;
		for(;dep[v]-dep[u]>=N;v=lst[v],d++) ans=max(ans,f[v][d]);
		for(d<<=8;v!=fa[u];v=fa[v],d++) ans=max(ans,d^a[v]);
		printf("%d\n",ans);
	}
}
posted @ 2022-01-11 12:03  C202044zxy  阅读(145)  评论(0编辑  收藏  举报