HDU5390.tree(线段树套Trie+空间复杂度优化)

HDU5390

给出一颗有根树。有点权。

两种操作:

1)0 u v,把点u的点权变成v

2)1 u,询问最大的\(v_u \oplus v_t\),要满足\(t\)\(u\)到根节点路径上的一个点。

做法:

先考虑没有修改的情况,用可持久化Trie可以很方便的做掉。

现在考虑带有修改的情况:

考虑在dfs序上建立线段树,我们对于线段树的每个节点(线段树的一个节点对应一个区间)建立一颗字典树。

一开始,每颗字典树保存节点区间内所有数的二进制形式。

假设我们当前要修改的区间为[l,r],在线段树上进行区间修改。

假设当前要修改的区间[l,r]覆盖了线段树上节点u所表示的区间,那么就在节点u的Trie上插入这个数或者删除这个数。即在Trie树上的某条链+1或-1。

在区间修改时,标记不做下传。

然后询问的时候,对于线段树的某个叶子节点。

查询它到根节点上的这条链。

如果Trie的某个子树从根节点开始往下求和,不为0,说明这个子树还是存在的。

但是这样空间复杂度爆炸,考虑优化空间复杂度。

Update:

证了一个晚上,终于想明白了。

对于区间修改操作,只在被这个区间完全覆盖的节点上记录这个操作。

对于单点询问操作,在这个叶子到根的这条链上全部记一下这个询问。

然后依次对线段树的每个节点按序处理操作,遇到询问就回答询问。

一个询问的答案,就是在每个相关节点被回答的答案中的最大值。

每处理过一个节点,就清空字典树,这样就是在反复利用一颗字典树。

如果两个操作公用一段区间,那么他们一定是在一个节点相遇,并且由于是按序插入,所以时间前后也没有问题,这样做一定是对的。

空间复杂度往下优化了一个log,询问离线。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int M=maxn*200;//20*1e5*30
vector<int> g[maxn];
int tr[M][2],tot,c[M],rt;
int n,m,dfn[maxn],cnt,sz[maxn];
long long qp[40];
int a[maxn];
int ins (int u,int x,int dep,int f) {
	if (dep==0) {
		if (!u) u=++tot;
		c[u]+=f;
		return u;
	}
	if (!u) u=++tot;
	c[u]+=f;
	if (x&qp[dep-1]) {
		tr[u][1]=ins(tr[u][1],x,dep-1,f);
	}
	else {
		tr[u][0]=ins(tr[u][0],x,dep-1,f);
	}
	return u;
}
void dfs (int u) {
	dfn[u]=++cnt;
	sz[u]=1;
	for (int v:g[u]) dfs(v),sz[u]+=sz[v]; 
}
struct qnode {
	int v,f,op,id;//0修改,1询问 
};
vector<qnode> opt[maxn<<2];
int ans[maxn];
void build (int i,int l,int r) {
	opt[i].clear();
	if (l==r) return;
	build(i<<1,l,(l+r)>>1);
	build(i<<1|1,((l+r)>>1)+1,r);
}
void query1 (int i,int l,int r,int L,int R,int v,int f,int op,int id) {
	if (l>=L&&r<=R) {
		opt[i].push_back({v,f,op,id});
		return;
	}
	int mid=(l+r)>>1;
	if (L<=mid) query1(i<<1,l,mid,L,R,v,f,op,id);
	if (R>mid) query1(i<<1|1,mid+1,r,L,R,v,f,op,id);
}
void query2 (int i,int l,int r,int p,int v,int f,int op,int id) {
	opt[i].push_back({v,f,op,id});
	if (l==r) return;
	int mid=(l+r)>>1;
	if (p<=mid) query2(i<<1,l,mid,p,v,f,op,id);
	if (p>mid) query2(i<<1|1,mid+1,r,p,v,f,op,id);
}
void solve (int i,int l,int r) {
	for (int j=0;j<=tot;j++) for (int k=0;k<2;k++) tr[j][k]=0,c[j]=0;
	tot=0;
	rt=0;
	for (qnode it:opt[i]) {
		if (it.op==0) {
			rt=ins(rt,it.v,32,it.f);	
		}
		else {
			int res=0;
			int u=rt;
			for (int i=31;i>=0;i--) {
				if (it.v&qp[i]) {
					if (c[tr[u][0]]) {
						res+=qp[i];
						u=tr[u][0];
					}
					else {
						u=tr[u][1];
					}
				}
				else {
					if (c[tr[u][1]]) {
						res+=qp[i];
						u=tr[u][1];
					}
					else {
						u=tr[u][0];
					}
				}
			}
			ans[it.id]=max(ans[it.id],res);
		}
	}
	if (l==r) return;
	int mid=(l+r)>>1;
	solve(i<<1,l,mid);
	solve(i<<1|1,mid+1,r);
}
int main () {
	qp[0]=1;
	for (int i=1;i<=31;i++) qp[i]=qp[i-1]*2;
	int _;
	scanf("%d",&_);
	while (_--) {
		scanf("%d%d",&n,&m);
		build(1,1,n);
		for (int i=1;i<=n;i++) g[i].clear();
		cnt=0;
		for (int i=2;i<=n;i++) {
			int x;
			scanf("%d",&x);
			g[x].push_back(i);
		}
		for (int i=1;i<=n;i++) scanf("%d",a+i);
		dfs(1);
		for (int i=1;i<=n;i++) query1(1,1,n,dfn[i],dfn[i]+sz[i]-1,a[i],1,0,0);
		for (int i=1;i<=m;i++) {
			ans[i]=-1;
			int op;
			scanf("%d",&op);
			if (op==0) {
				int u,v;
				scanf("%d%d",&u,&v);
				query1(1,1,n,dfn[u],dfn[u]+sz[u]-1,a[u],-1,0,0);
				a[u]=v;
				query1(1,1,n,dfn[u],dfn[u]+sz[u]-1,a[u],1,0,0);
			}
			else {
				int u;
				scanf("%d",&u);
				query2(1,1,n,dfn[u],a[u],0,1,i);
			}
		}
		solve(1,1,n);
		for (int i=1;i<=m;i++) if (ans[i]!=-1) printf("%d\n",ans[i]);
	}
}

。。。没想明白

这里先给出在线版本,被卡空间了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int M=maxn*40;
vector<int> g[maxn];
int T[M];//每个字典树的根节点
int tr[M][2],tot,c[M];
int n,m,dfn[maxn],cnt,sz[maxn];
long long qp[40];
int a[maxn];
int ins (int u,int x,int dep,int f) {
	if (dep==0) {
		if (!u) u=++tot;
		c[u]+=f;
		return u;
	}
	if (!u) u=++tot;
	c[u]+=f;
	if (x&qp[dep-1]) {
		tr[u][1]=ins(tr[u][1],x,dep-1,f);
	}
	else {
		tr[u][0]=ins(tr[u][0],x,dep-1,f);
	}
	return u;
}

void up (int i,int l,int r,int L,int R,int v,int f) {
	if (l>=L&&r<=R) {
		T[i]=ins(T[i],v,32,f);
		return;
	}
	int mid=(l+r)>>1;
	if (L<=mid) up(i<<1,l,mid,L,R,v,f);
	if (R>mid) up(i<<1|1,mid+1,r,L,R,v,f);
}
vector<int> path;
void query (int i,int l,int r,int p) {
	path.push_back(T[i]);
	if (l==r) return;
	int mid=(l+r)>>1;
	if (p<=mid) query(i<<1,l,mid,p);
	if (p>mid) query(i<<1|1,mid+1,r,p);
}
void dfs (int u) {
	dfn[u]=++tot;
	sz[u]=1;
	for (int v:g[u]) dfs(v),sz[u]+=sz[v]; 
}
int main () {
	qp[0]=1;
	for (int i=1;i<=31;i++) qp[i]=qp[i-1]*2;
	int _;
	scanf("%d",&_);
	while (_--) {
		scanf("%d%d",&n,&m);
		for (int i=1;i<=n;i++) g[i].clear();
		cnt=0;
		for (int i=0;i<=tot;i++) {
			c[i]=0;
			for (int j=0;j<2;j++) tr[i][j]=0;
		}
		tot=0;
		for (int i=2;i<=n;i++) {
			int x;
			scanf("%d",&x);
			g[x].push_back(i);
		}
		for (int i=1;i<=n;i++) scanf("%d",a+i);
		dfs(1);
		for (int i=1;i<=n;i++) {
			up(1,1,n,dfn[i],dfn[i]+sz[i]-1,a[i],1);
		}
		while (m--) {
			int op;
			scanf("%d",&op);
			if (op==0) {
				int u,v;
				scanf("%d%d",&u,&v);
				up(1,1,n,dfn[u],dfn[u]+sz[u]-1,a[u],-1);
				a[u]=v;
				up(1,1,n,dfn[u],dfn[u]+sz[u]-1,a[u],1);
			}
			else {
				int u;
				scanf("%d",&u);
				path.clear();
				query(1,1,n,dfn[u]);
				int x=a[u];
				int ans=0;
				for (int i=31;i>=0;i--) {
					if (x&qp[i]) {
						int sum=0;
						for (int j=0;j<path.size();j++) {
							sum+=c[tr[path[j]][0]];
						}
						if (sum>0) {
							ans+=qp[i];
							for (int j=0;j<path.size();j++) {
								path[j]=tr[path[j]][0];
							}
						}
						else {
							for (int j=0;j<path.size();j++) {
								path[j]=tr[path[j]][1];
							}
						}
					} 
					else {
						int sum=0;
						for (int j=0;j<path.size();j++) {
							sum+=c[tr[path[j]][1]];
						}
						if (sum>0) {
							ans+=qp[i];
							for (int j=0;j<path.size();j++) {
								path[j]=tr[path[j]][1];
							}
						}
						else {
							for (int j=0;j<path.size();j++) {
								path[j]=tr[path[j]][0];
							}
						}
					}
				}
				printf("%d\n",ans);
			}
		}
	}
}
posted @ 2021-10-23 01:42  zlc0405  阅读(86)  评论(0编辑  收藏  举报