萬代在英國註冊《風之克羅諾亞》新商標 引發新作猜想
阿新 • • 發佈:2021-12-20
一、題目
二、解法
感冒在家兩天,今天才回學校,雖然部落格鴿了一天但是我換籤名了。
對於詢問其實可以分塊,每一塊的前 \(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\)
預處理的複雜度是 \(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); } }