[可持久化Trie][HDU4757] Tree
阿新 • • 發佈:2018-07-29
amp ++ lca 需要 tdi 怎麽 clu mmm 自己
恩,剛學了一發可持久化Trie樹,其實挺簡單的。。
反正可持久化數據結構都一個樣嘛,動態加點就好了。
還是寫一篇博客給自己看吧。
因為樹上的路徑嘛,肯定要想到把路徑分成兩部分,x->lca(x,y) 和 y->lca(x,y) 。 這就相當與兩塊區間,然後求單點異或最大值,自然就想到可持久化了唄。(想想你怎麽用可持久化線段樹求區間第k大(小)就好了)。只需要按照遍歷順序把點權以二進制的形式插入Trie樹。新的版本從它的父親節點的版本那繼承就好了。
(p:我用倍增求的lca)
插入的時候把路徑上的每個點的cnt+1,這樣處理,你用x的版本減去lca(x,y)的版本就可以得到了x->lca(x,y)路徑上有哪些點的點權啦。
然後根據Trie樹貪心就好,盡量使每一位都和z不同。
因為trie樹存的是二進制嘛,如果,z的某一位是1的話,你就看有沒有這一位是0的數,如果有就走ch[0],不然你就只能走ch[1]了。。
這樣滿足高位最大,答案就最優,所以你的trie樹肯定從高位存到低位啦,最高位設為17就好,反正最大才16。
emmmmm... 然後感覺就沒什麽了,不過有個細節。
多組數據 多組數據 多組數據
重要的事說三遍,不要問我WA了多少次。。
如果你有點兒迷糊,去敲一敲可持久化線段樹就好了。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define LL long long #define pch putchar #define gch getchar #define mem(k) memset(k,0,sizeof k) using namespace std; const int MAXN=100000+5; const int MAXM=100000+5; int n,m; int cnt,root[MAXN]; struct Tree{ int ch[2],pos,cnt; }tree[MAXN*2*20+MAXM*20];//空間你就估摸著開吧,每次插入新增17個點。 int head[MAXN],cou,a[MAXN],d[MAXN],p[MAXN][20]; struct ROAD{ int v,next; }road[MAXN<<1]; inline int insert(int x,int pre,int pos){//可持久化插入操作。 int now=++cnt; tree[now].pos=pos; tree[now].ch[0]=tree[pre].ch[0]; tree[now].ch[1]=tree[pre].ch[1]; tree[now].cnt=tree[pre].cnt+1; if(pos==0) return now; if(1<<(pos-1)&x) tree[now].ch[1]=insert(x,tree[pre].ch[1],pos-1);//判斷下一位。 else tree[now].ch[0]=insert(x,tree[pre].ch[0],pos-1); return now; } inline void add_edge(int x,int y){//加邊。 road[++cou].v=y; road[cou].next=head[x]; head[x]=cou; } inline void dfs(int now,int fa){//遍歷原樹,處理出稀疏表,把點權插進Trie樹,當然是可持久化的操作。 root[now]=insert(a[now],root[fa],17); d[now]=d[fa]+1; p[now][0]=fa; for(int i=1;i<=17;++i){ p[now][i]=p[p[now][i-1]][i-1]; } for(int i=head[now];i!=-1;i=road[i].next){ int v=road[i].v; if(fa==v) continue; dfs(v,now); } } inline int LCA(int x,int y){//倍增LCA。 if(d[x]<d[y]) swap(x,y); for(int i=17;i>=0;--i){ if(d[p[x][i]]<d[y]) continue; x=p[x][i]; if(d[x]==d[y]) break; } if(x==y) return x; for(int i=17;i>=0;--i){ if(p[x][i]!=p[y][i]){ x=p[x][i]; y=p[y][i]; } } return p[x][0]; } inline int ask(int now,int pre,int x){//Trie樹上做減法。 int pos=tree[now].pos; if(pos==0) return 0; int next=(1<<(pos-1)&x)?0:1;//你想要的數。 int son[2]; son[0]=tree[tree[now].ch[0]].cnt-tree[tree[pre].ch[0]].cnt; son[1]=tree[tree[now].ch[1]].cnt-tree[tree[pre].ch[1]].cnt; int ans=0; if(son[next]) ans^=(1<<(pos-1)),ans^=ask(tree[now].ch[next],tree[pre].ch[next],x); else if(son[next^1]) ans^=ask(tree[now].ch[next^1],tree[pre].ch[next^1],x); else return 0; return ans; } inline int query(int x,int y,int z){//分兩部分 分別 ask 就好了。 int ans=0;int lca=LCA(x,y); ans=a[lca]^z; ans=max(ans,ask(root[x],root[lca],z)); ans=max(ans,ask(root[y],root[lca],z)); return ans; } int main(){ while(scanf("%d%d",&n,&m)!=EOF){ cnt=0;cou=0; mem(root);mem(a);mem(d);mem(p); memset(head,-1,sizeof head); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<n;++i){ int x,y;scanf("%d%d",&x,&y); add_edge(x,y);add_edge(y,x); } dfs(1,0); for(int i=1;i<=m;++i){ int x,y,z;scanf("%d%d%d",&x,&y,&z); printf("%d\n",query(x,y,z)); } } return 0; }
[可持久化Trie][HDU4757] Tree