【值域分块(阈值)+Trie】CF840E In a Trap

【题目】
CF
一棵 n n n个节点的树,点有权 a i a_i ai Q Q Q次询问 ( u , v ) (u,v) (u,v),其中 u u u v v v祖先,问路径上所有节点 i i i max ⁡ { a i ⨁ d i s ( i , v ) } \max \{a_i \bigoplus dis(i,v)\} max{aidis(i,v)} n , a i ≤ 5 × 1 0 4 , Q ≤ 1.5 × 1 0 5 n,a_i\leq 5\times 10^4,Q\leq 1.5\times 10^5 n,ai5×104,Q1.5×105

【解题思路】
十分有趣的阈值。

一个数最多 16 16 16位,如果我们将 v v v u u u的路径每 256 256 256个节点分成一组,那么对于同一段, d i s dis dis在二进制下从低到高第 8 8 8位以后都是相同的。

于是我们预处理每个点往上 256 256 256个节点 a i a_i ai二进制下高位的 Trie \text{Trie} Trie,并以此维护只考虑最低 8 8 8位时点权异或对应距离的最大值。即对于一个节点 i i i和它上面的节点 j j j j j j i i i Trie \text{Trie} Trie中插入的值应为 ( ( d e p i − d e p j ) ⨁ a j ) & 255 ((dep_i-dep_j)\bigoplus a_j)\&255 ((depidepj)aj)&255

那么对于一段 256 256 256个节点,我们可以在 Trie \text{Trie} Trie上贪心地走,然后再根据对应节点的值更新答案。

具体实现上,我们并不需要写一个 Trie \text{Trie} Trie,可以直接记一个数组 f x , i f_{x,i} fx,i表示 x x x这个节点,高 8 8 8位数为 i i i的答案即可,这样用位运算可以方便表示。

c = 256 c=256 c=256,与 n \sqrt n n 同阶,那么复杂度就是 O ( n c log ⁡ c + Q c ) O(nc\log c+Qc) O(nclogc+Qc)

【参考代码】

#include
using namespace std;

const int N=5e4+10,M=256;

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
	void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;

namespace Graph
{
	int tot,head[N],dep[N],fa[N],jump[N];
	struct Tway{int v,nex;}e[N<<1];
	void add(int u,int v)
	{
		e[++tot]=(Tway){v,head[u]};head[u]=tot;
		e[++tot]=(Tway){u,head[v]};head[v]=tot;
	}
}
using namespace Graph;

namespace DreamLolita
{
	int n,Q,a[N],f[N][M];
	void init()
	{
		n=read();Q=read();
		for(int i=1;i<=n;++i) a[i]=read();
		for(int i=1;i<n;++i) add(read(),read());
	}
	void dfs(int x)
	{
		for(int i=head[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(v==fa[x]) continue;
			fa[v]=x;dep[v]=dep[x]+1;dfs(v);
		}
	}
	void gmax(int &x,int y){x=max(x,y);}
	void solve()
	{
		dep[1]=1;dfs(1);
		for(int i=1;i<=n;++i)
		{
			int x=i;
			for(int j=0;j<M && x;++j,x=fa[x])
			{
				int t=(a[x]^j)>>8;
				gmax(f[i][(M-1)^t],(a[x]^j)|((M-1)<<8));
			}
			jump[i]=x;
			for(int j=0;j<8;++j) for(int k=0;k<M;++k) 
				if(f[i][k^(1<<j)]) gmax(f[i][k],f[i][k^(1<<j)]^(1<<(j+8)));
		}
		while(Q--)
		{
			int u=read(),v=read(),x=v,ans=a[u]^(dep[v]-dep[u]);
			for(int i=0;dep[u]<=dep[jump[x]];++i,x=jump[x]) ans=max(ans,f[x][i]);
			for(;dep[u]<=dep[x];x=fa[x]) ans=max(ans,(dep[v]-dep[x])^a[x]);
			writeln(ans);
		}
	}
	void solution(){init();solve();}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("CF840E.in","r",stdin);
	freopen("CF840E.out","w",stdout);
#endif
	DreamLolita::solution();
	return 0;
}

你可能感兴趣的:(分而治之-分块,字符串-Trie,其他-阈值)