1. 程式人生 > 遊戲 >萬代在英國註冊《風之克羅諾亞》新商標 引發新作猜想

萬代在英國註冊《風之克羅諾亞》新商標 引發新作猜想

一、題目

點此看題

二、解法

感冒在家兩天,今天才回學校,雖然部落格鴿了一天但是我換籤名了

對於詢問其實可以分塊,每一塊的前 \(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);
	}
}