可持久化字典树

可持久化字典树


对于数列的一段区间,或是树上的路径,从中找出一个数,使得它异或z的值最大。对于其他相似的,可以通过前缀异或和的方式转化成此类。

基本思想:

  1. 以每个结点为根,建一颗字典树(内容为1到i的值)。这样之后,做差后,即为一段区间或是一段路径。
  2. 可以发现,如果裸着建,不仅要消耗很多的时间,更是要消耗很多的空间。考虑以i为根的字典树和以(i-1)为跟的字典树的异同。
  3. 可以发现,在当前以i为根的字典树上减去a[i],就是(i-1)的字典树了。所以,我们可以将除了a[i]之外的结点都连到(i-1)的树上。

    当然,i-1的树也是从i-2的树上引用过来的。

  4. 这样之后,会出现一个问题。比如,a[i-1]的值与a[i]的值相同,那么我们到底要不要跟新呢?还是直接将root[i]全部指向root[i-1]?
    还是之前的做法。除了a[i]的值,其他的值全部引用前面的,将a[i]跟新root[i]。
    这样做完之后,可以发现,如果不加以区分,root[i]和root[i-1]的树一模一样,直接做差之后不就没有了。可是事实上,在【i,i】这给区间内,有一个数的值为a[i]。
    所以我们用一个sum数组加以区别,也就是说,如果当前的root的结点是引用前面的,那么sum不变。否则(就是a[i]跟新的部分),sum[root[i]]=sum[[root[i-1]]+1。a[i]这个数的每个位置都是之前的+1。
  5. 这样我们在判断区间的时候,就可以拿sum[root[r]]-sum[root[l-1]]。如果大于0,说明在root[l-1]之后,被跟新过,所以可以取。如果sum值相同,代表root[r]中的那部分是直接引用的root[l-1]中的那部分,这个区间内没有跟新过,所以无法取。

bzoj 3261

  • 给定一个非负整数序列{a},初始长度为N。
    有M个操作,有以下两种操作类型:
    1、Ax:添加操作,表示在序列末尾添加一个数x,序列的长度N+1。
    2、Qlrx:询问操作,你需要找到一个位置p,满足l<=p<=r,使得:
    a[p] xor a[p+1] xor ... xor a[N] xor x 最大,输出最大是多少。

  • 查询的部分为sum[p-1] xor sum[N] xor x。sum[N] xor x 直接可得,所以即寻找一个p-1,使得结果最大(l<=p<=r)。

  • 直接那前缀异或和做字典树,由于最开始插入了root[1]的值为0,也就是root[1]代表的是sum[0],所以我们本来应该是查找root[l-2],root[r-1],现在应该是root[l-1],root[r]。
  • 查询即可。

//要查询的是sum[n]^sum[k-1]^x 最大。
//从k开始异或,那么l-1<=k-1<=r-1 所以看以r-1为根的树是在以l-2为根的树上跟新过
//                                 所以应该是root[r],root[l-1] 

#include"stdio.h"
#define N 30050000
int tree[N][2];
int root[N];
int l;
int sum[N];
int d[40]; 
int cnt;
void split(int k){
	int i;
	l=0;
	while(k>0) {
		l++;
		d[l]=k%2;
		k=k/2;
	}
	for (i=l+1;i<=27;i++) d[i]=0;
	return;
}
void build(int x,int &y){
	int i;
	cnt++;
	y=cnt;
	sum[y]=sum[x]+1;
	int now=y;
	for (i=27;i>=1;i--) {
		tree[now][d[i]^1]=tree[x][d[i]^1];
		cnt++;
		tree[now][d[i]]=cnt;
		now=cnt;x=tree[x][d[i]];
		sum[now]=sum[x]+1;
	}
}
int query(int x,int y) {
	int ans=0;
	int i;
	for (i=27;i>=1;i--) {
		if (sum[tree[y][d[i]^1]]-sum[tree[x][d[i]^1]]>0) {
			ans=ans+(1<<(i-1));
			y=tree[y][d[i]^1];x=tree[x][d[i]^1];
		}else {
			y=tree[y][d[i]];x=tree[x][d[i]];
		}
	}
	return ans;
}
char s[10];
int main(){
	int n,m,l,r,sum,x,k,i;
	int ans;
	cnt=0;
	scanf("%d %d",&n,&m);
	split(0);
	tree[0][0]=0;
	tree[0][1]=0;
	root[0]=0;
	build(root[0],root[1]);
	n++;
	sum=0;
	for (i=2;i<=n;i++) {
		scanf("%d",&x);
		sum=sum^x;
		split(sum);
		build(root[i-1],root[i]);
	}
	for (i=1;i<=m;i++) {
		scanf("%s",s);  
		if (s[0]=='A') {
			n++;
			scanf("%d",&x);
			sum=sum^x;
			split(sum);
			build(root[n-1],root[n]);
		}else {
			scanf("%d %d %d",&l,&r,&k);
			k=k^sum;
			split(k);
			ans=query(root[l-1],root[r]);
			printf("%d
",ans);
		}
	}
}


hdu 4757

  • 给定一棵树,树上每个点都有一个权值,给两个点,问从l到r的路径上那个点异或z的值最大。
  • 可持久化字典树+lca。

  • 可以发现,树可以本拆分为一条一条从根到叶子的链。但这儿,不用这么麻烦。直接root[i]引用root[father[i]]即可。最后的答案为max(query(root[father[rmq(l,r)]],root[l]),query(root[father[rmq(l,r)]],root[r])),也就是树上的两个区间的最大值。

    
    /*
    有一棵树,树上每个点都有一个权值,给两个点,问从l到r的路径上那个点异或z的值最大。 
    
    把每个当成是一颗字典树,从当前点向father连边,跟新自己 
    
    */
    
    #include"stdio.h"
    #include"string.h"
    #define N 10050000
    int tree[N][2];
    int root[N];
    int ll;
    int sum[N];
    int d[40]; 
    int cnt;
    
    struct node{
    	int v,next;
    }edge[200050];
    int head[100050];
    int dp[1000050][30];//rmq 
    int find[1000050];//保存搜索的路径 用来rmq 
    int visit[100050]; 
    int l,count;
    int start[100050];//保存每个点最开始被搜索到的位置 
    int rk[100050];
    int id[100050];
    int father[100050];
    int a[100050];
    int ss;
    int min(int a,int b){
    	if (a>b) return b;return a;
    }
    int max(int a,int b) {
    	if (a>b) return a;return b;
    }
    void add(int x,int y){
    	count++;
    	edge[count].v=y;
    	edge[count].next=head[x];
    	head[x]=count;
    }
    void dfs(int k){
    	int i,v;
    	if (rk[k]==-1) {
    		ss++;
    		rk[k]=ss;
    		id[ss]=k;
    	}
    	for (i=head[k];i!=-1;i=edge[i].next){
    		v=edge[i].v;
    		if (visit[v]==0){
    			father[v]=k;
    			visit[v]=1;
    			l++;find[l]=rk[k];if (start[k]==-1) {start[rk[k]]=l;}
    			dfs(v);	
    		}
    	}
    	 {l++;find[l]=rk[k];if (start[rk[k]]==-1) {start[rk[k]]=l;}}
    }
    void initrmq(int n){
    	int i,j;
    	for (i=1;i<=n;i++) dp[i][0]=find[i];
    	for (j=1;(1<<j)<=n;j++)
    	for (i=1;(i+(1<<j))-1<=n;i++) dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
    }
    int rmq(int l,int r){
    	int k;
    	k=0;
    	while(((1<<(k+1))+l-1)<=r) k++;
    	return min(dp[l][k],dp[r-(1<<k)+1][k]);
    }
    void split(int k){
    	int i;
    	ll=0;
    	while(k>0) {
    		ll++;
    		d[ll]=k%2;
    		k=k/2;
    	}
    	for (i=ll+1;i<=25;i++) d[i]=0;
    	return;
    }
    void build(int x,int &y){
    	int i;
    	cnt++;
    	y=cnt;
    	sum[y]=sum[x]+1;
    	int now=y;
    	for (i=25;i>=1;i--) {
    		tree[now][d[i]^1]=tree[x][d[i]^1];
    		cnt++;
    		tree[now][d[i]]=cnt;
    		now=cnt;x=tree[x][d[i]];
    		sum[now]=sum[x]+1;
    	}
    }
    int query(int x,int y) {
    	int ans=0;
    	int i;
    	for (i=25;i>=1;i--) {
    		if (sum[tree[y][d[i]^1]]-sum[tree[x][d[i]^1]]>0) {
    			ans=ans+(1<<(i-1));
    			y=tree[y][d[i]^1];x=tree[x][d[i]^1];
    		}else {
    			y=tree[y][d[i]];x=tree[x][d[i]];
    		}
    	}
    	return ans;
    }
    void ff(int k,int fa){
    	int i,v;
    	for (i=head[k];i!=-1;i=edge[i].next) {
    		v=edge[i].v;
    		if (v!=fa) {
    			split(a[v]);
    			build(root[k],root[v]);
    			ff(v,k);
    		}
    	}
    }
    char s[10];
    int main(){
    	int n,m,sum,x,y,z,lca,k,i,xx,yy,q;
    	int ans1,ans2;
    	while(scanf("%d %d",&n,&q)!=EOF){
    	cnt=0;count=0;
    	memset(head,-1,sizeof(head));
    	for (i=1;i<=n;i++) {
    		scanf("%d",&a[i]);
    	}
    	for (i=1;i<=n-1;i++) {
    		scanf("%d %d",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
    	l=0;
        memset(start,-1,sizeof(start));
        memset(visit,0,sizeof(visit));
        memset(rk,-1,sizeof(rk));
        memset(id,-1,sizeof(id));
        memset(father,-1,sizeof(father));
        visit[1]=1;
        ss=0;
        father[1]=0;
        dfs(1);
        initrmq(l);
        
    	split(a[1]);
    	tree[0][0]=0;
    	tree[0][1]=0;
    	root[0]=0;
    	build(root[0],root[1]);
    	n++;
    	ff(1,-1);
    	for (i=1;i<=q;i++) {
    		scanf("%d %d %d",&x,&y,&z);
    		split(z);
    		xx=start[rk[x]];
    		yy=start[rk[y]];
    		int tmp;
    		if (xx>yy) {tmp=xx;xx=yy;yy=tmp;}
    		lca=id[rmq(xx,yy)];
    		ans1=query(root[father[lca]],root[x]);
    		ans2=query(root[father[lca]],root[y]);
    		printf("%d
    ",max(ans1,ans2));
    	}
    	}
    }
    
原文地址:https://www.cnblogs.com/nowheretrix/p/9341258.html