• [bzoj3306]树_dfs序_线段树_倍增lca


    树 bzoj-3306

    题目大意:给定一颗n个节点的树,支持换根、修改点权、查询子树最小值。

    注释:$1le n,qle 10^5$。


    想法

    如果没有换根操作,就是$dfs$序+线段树维护区间最小值即可。

    加入有换根操作,我们发现对修改操作没影响。

    我们只需要判断一下询问的点和当前根的关系即可。

    Code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ls p<<1
    #define rs p<<1|1
    #define N 100010 
    using namespace std;
    int to[N<<1],nxt[N<<1],tot,head[N];
    int f[21][N],dic[N],re[N],size[N],dep[N],cnt,mn[N<<2],val[N],root;
    inline void add(int x,int y) {to[++tot]=y; nxt[tot]=head[x]; head[x]=tot;}
    void dfs(int pos,int fa)
    {
    	dic[pos]=++cnt; re[cnt]=pos; f[0][pos]=fa; for(int i=1;i<=20;i++) f[i][pos]=f[i-1][f[i-1][pos]];
    	dep[pos]=dep[fa]+1; size[pos]=1; for(int i=head[pos];i;i=nxt[i]) if(to[i]!=fa)
    	{
    		dfs(to[i],pos);
    		size[pos]+=size[to[i]];
    	}
    }
    int lca(int x,int y)
    {
    	if(dep[x]<dep[y]) swap(x,y);
    	for(int i=20;~i;i--) if(dep[f[i][x]]>=dep[y]) x=f[i][x];
    	if(x==y) return x;
    	for(int i=20;~i;i--) if(f[i][x]!=f[i][y]) x=f[i][x],y=f[i][y];
    	return f[0][x];
    }
    int get_lca(int x,int y)
    {
    	if(dep[x]<dep[y]) swap(x,y);
    	for(int i=20;~i;i--) if(dep[f[i][x]]>dep[y]) x=f[i][x];
    	return x;
    }
    inline void pushup(int p)
    {
    	mn[p]=min(mn[ls],mn[rs]);
    }
    void build(int l,int r,int p)
    {
    	if(l==r) {mn[p]=val[re[l]]; return;}
    	int mid=(l+r)>>1;
    	build(l,mid,ls); build(mid+1,r,rs);
    	pushup(p);
    }
    void update(int x,int v,int l,int r,int p)
    {
    	if(l==r) {mn[p]=v; return;}
    	int mid=(l+r)>>1;
    	if(x<=mid) update(x,v,l,mid,ls);
    	else update(x,v,mid+1,r,rs);
    	pushup(p);
    }
    int query(int x,int y,int l,int r,int p)
    {
    	if(x<=l&&r<=y) return mn[p];
    	int mid=(l+r)>>1,ans=0x7f7f7f7f;
    	if(x<=mid) ans=min(ans,query(x,y,l,mid,ls));
    	if(mid<y) ans=min(ans,query(x,y,mid+1,r,rs));
    	return ans;
    }
    int main()
    {
    	int x,y,n,m; scanf("%d%d",&n,&m); char opt[10]; for(int i=1;i<=n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		if(!x) root=i;
    		else add(i,x),add(x,i);
    		val[i]=y;
    	}
    	dfs(root,root);
    	// for(int i=1;i<=n;i++) printf("%d ",size[i]); puts("");
    	build(1,n,1);
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%s%d",opt,&x);
    		if(opt[0]=='V')
    		{
    			scanf("%d",&y); update(dic[x],y,1,n,1);
    		}
    		else if(opt[0]=='E')
    		{
    			root=x;
    		}
    		else
    		{
    			if(x==root) printf("%d
    ",query(1,n,1,n,1));
    			else if(lca(x,root)!=x) printf("%d
    ",query(dic[x],dic[x]+size[x]-1,1,n,1));
    			else
    			{
    				int y=get_lca(root,x);
    				printf("%d
    ",min(query(1,dic[y]-1,1,n,1),query(dic[y]+size[y],n,1,n,1)));
    			}
    		}
    	}
    	return 0;
    }
    

    小结:拟对象考虑问题有奇效。

  • 相关阅读:
    Leetcode 694. 不同岛屿的数量 中等 回溯 岛屿问题
    集成电路设计流程
    Leetcode 44. 通配符匹配 困难 动态规划 精选 TOP 面试题
    Leetcode 13. 罗马数字转整数 简单 字符串
    Leetcode 36. 有效的数独 中等 数组遍历 精选 TOP 面试题
    windows 不是真正的多用户OS,linux才是
    java AWT弹球游戏
    java AWT 图片查看器
    java AWT 简易绘图
    java Swing 进度条
  • 原文地址:https://www.cnblogs.com/ShuraK/p/10198717.html
Copyright © 2020-2023  润新知