zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",ans);
    			}
    		}
    	}
    }
  • 相关阅读:
    解析大型.NET ERP系统 权限模块设计与实现
    Enterprise Solution 开源项目资源汇总 Visual Studio Online 源代码托管 企业管理软件开发框架
    解析大型.NET ERP系统 单据编码功能实现
    解析大型.NET ERP系统 单据标准(新增,修改,删除,复制,打印)功能程序设计
    Windows 10 部署Enterprise Solution 5.5
    解析大型.NET ERP系统 设计异常处理模块
    解析大型.NET ERP系统 业务逻辑设计与实现
    解析大型.NET ERP系统 多国语言实现
    Enterprise Solution 管理软件开发框架流程实战
    解析大型.NET ERP系统 数据审计功能
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15441199.html
Copyright © 2011-2022 走看看