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);
    			}
    		}
    	}
    }
  • 相关阅读:
    多选下拉框带搜索(aps.net)
    asp.net无刷新上传(带预览)
    http免费升级https 攻略(超简单)
    用JS获取地址栏参数的方法
    C#生成二维码
    update更新另一个字段
    自适应瀑布型布局(手机,PC全兼容)
    微信扫码支付.net版本
    常用css3技巧
    Repeater 嵌套,子级Repeater获取 父级Repeater 中的值
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15441199.html
Copyright © 2011-2022 走看看