zoukankan      html  css  js  c++  java
  • [可持久化Trie][HDU4757] Tree

    恩,刚学了一发可持久化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
    ",query(x,y,z));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    mysqllog
    清理:db上面的过期的binlog,释放磁盘空间。 (转)
    linux下shell命令trap
    mvc
    uci随笔
    luci 随笔
    shell脚本 整数比较
    lua学习
    OPENWRT make menuconfig错误之一
    openwrt 中make的使用
  • 原文地址:https://www.cnblogs.com/jacktangs/p/9384840.html
Copyright © 2011-2022 走看看