• P3979 遥远的国度


    P3979 遥远的国度

    长者的题qwq

    在没有换根之前,都是可以裸的树剖维护的

    一个最普通的询问

    如果我们换一次根

    就会变成一下情况

    然而我们所查询的子树的结构并没有发生改变,为什么呢?

    我们可以观察到,所查询的根并没有在当前的根到原来跟的路径上。

    可以想象成你将所换的根提起来,在提的时候,所有结构发生改变的子树的根,都是所换根到原来根的路径上的节点,因为他们的子树中的节点要成为他们的父节点(or级别更高,不知道高到哪里去了

    就像上面一样

    查询时就是查询红线以外的的节点

    ]

    看起来很简单的样子qwq,不就是如果查询节点的(A)和当前根节点的(R)(Lca)(A)的时候,就查询(A)的祖先and其祖先和(A)除了(R)所在的
    子树,以外的所有节点么。

    不过怎么查呢?

    不要忘了这事一道树剖题,可以利用(dfs)序来进行

    一颗子树中的(dfs)序是连续的
    若一个节点(A)在一个以(R)为根的子树中
    (dfs)序大于(R)(dfs)序,小于(R)(dfs)+(R)的子树大小。

    所以我们可以枚举(A)的儿子,再根据上面这一条定理判断

    然后如何使用树剖维护呢

    还是根据一棵树中的(dfs)序是连续的来做。

    如果我们将(R)所在的子树的链在线段树中都删去,那么剩下的链就是我们需要查询的区间。 所以我们只需要查询两次就可以了

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    using std::swap;
    using std::min;
    const int maxn=101000;
    int n,m,r,now_r;
    struct node
    {
        int p;
        int nxt;
    };
    node line[maxn<<1];
    int head[maxn],tail;
    void add(int a,int b)
    {
        line[++tail].p=b;
        line[tail].nxt=head[a];
        head[a]=tail;
    }
    int dep[maxn],id[maxn],f[maxn],top[maxn];
    int son[maxn],size[maxn],num;
    int val[maxn],base[maxn];
    int t[maxn<<2],tag[maxn<<2];
    void build(int root,int l,int r)
    {
        if(l==r)
        {
            t[root]=base[l];
            return ;
        }
        int mid=(l+r)>>1;
        build(root<<1,l,mid);
        build(root<<1|1,mid+1,r);
        t[root]=min(t[root<<1],t[root<<1|1]);
    }
    void push_down(int root,int l,int mid,int r)
    {
        if(!tag[root])	return ;
        int ls=root<<1,rs=root<<1|1;
        tag[ls]=tag[root];
        tag[rs]=tag[root];
        t[ls]=tag[root];
        t[rs]=tag[root];
        tag[root]=0;
        return ;
    }
    void updata(int root,int l,int r,int al,int ar,int val)
    {
        if(l>ar||r<al)	return ;
        if(l>=al&&r<=ar)
        {
            t[root]=val;
            tag[root]=val;
            return ;
        }
        int mid=(l+r)>>1;
        push_down(root,l,mid,r);
        updata(root<<1,l,mid,al,ar,val);
        updata(root<<1|1,mid+1,r,al,ar,val);
        t[root]=min(t[root<<1],t[root<<1|1]);
    }
    int query(int root,int l,int r,int al,int ar)
    {
        if(l>ar||r<al)	return 0x7fffffff;
        if(l>=al&&r<=ar)	return t[root];
        int mid=(l+r)>>1;
        push_down(root,l,mid,r);
        return 	min(query(root<<1,l,mid,al,ar),
                query(root<<1|1,mid+1,r,al,ar));
    }
    /*----------------------------------------------------线段树*/
    void dfs1(int now,int fa,int d)
    {
        dep[now]=d;
        size[now]=1;
        f[now]=fa;
        int W=-1;
        for(int i=head[now];i;i=line[i].nxt)
            if(line[i].p!=fa)
            {
                dfs1(line[i].p,now,d+1);
                size[now]+=size[line[i].p];
                if(size[line[i].p]>W)
                    son[now]=line[i].p,W=size[line[i].p];
            }
    }
    void dfs2(int now,int tf)
    {
        top[now]=tf;
        id[now]=++num;
        base[num]=val[now];
        if(!son[now])	return ;
        dfs2(son[now],tf);
        for(int i=head[now];i;i=line[i].nxt)
            if(line[i].p!=f[now]&&line[i].p!=son[now])
                dfs2(line[i].p,line[i].p);	
    }
    void seg_updata(int a,int b,int c)
    {
        while(top[a]!=top[b])
        {
            if(dep[top[a]]<dep[top[b]])	swap(a,b);
            updata(1,1,num,id[top[a]],id[a],c);
            a=f[top[a]];
        }
        if(dep[a]>dep[b])	swap(a,b);
        updata(1,1,num,id[a],id[b],c);
        return ;
    }
    int lca(int a,int b)
    {
        while(top[a]!=top[b])
        {
            if(dep[top[a]]<dep[top[b]])	swap(a,b);
            a=f[top[a]];
        }
        return dep[a] < dep [b] ? a : b ;
    }
    int tree_query(int a)
    {
        if(a==now_r)	//如果是当前根,直接输出就可以了
            return query(1,1,num,1,num);
        int Lca=lca(now_r,a);//树剖求lca
        if(Lca!=a)//如果是第一种情况
        	return query(1,1,num,id[a],id[a]+size[a]-1);//直接模板一顿套
        int S;
        for(int i=head[a];i;i=line[i].nxt)
        	if(id[line[i].p]<=id[now_r]&&id[line[i].p]+size[line[i].p]-1>=id[now_r]&&line[i].p!=f[a])
        	{
        		S=line[i].p;
        		break;
        	}//使用规律进行查找,这里可以使用倍增加速
        return min(query(1,1,num,1,id[S]-1),query(1,1,num,id[S]+size[S],num));//分成两段进行查询
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        int a,b,c;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&a,&b);
            add(a,b);add(b,a);
        }
        for(int i=1;i<=n;i++)
            scanf("%d",&val[i]);
        scanf("%d",&r);
        now_r=r;
        dfs1(r,0,1);
        dfs2(r,r);
        build(1,1,num);
        for(int i=1;i<=m;i++)
        {/*
            scanf("%d%d",&a,&b);
            printf("%d
    ",lca(a,b));*/
            scanf("%d",&a);
            if(a==1)
            {
                scanf("%d",&b);
                now_r=b;
                continue;
            }
            if(a==2)
            {
                scanf("%d%d%d",&a,&b,&c);
                seg_updata(a,b,c);
                continue;
            }
            if(a==3)
            {
                scanf("%d",&b);
                printf("%d
    ",tree_query(b));
                continue;
            }
        }
    }
    
  • 相关阅读:
    《MySQL技术内幕》读书笔记
    CentOS裸机环境下安装php-7.3.1
    docker容器和本机互传文件
    一些数组操作函数记录
    window下强制删除文件
    根据一个镜像启动一个容器
    Nginx配置命令大全
    《Linux.Shell编程从入门到精通》读书笔记
    share drive 无效
    vi替换字符串
  • 原文地址:https://www.cnblogs.com/Lance1ot/p/9390577.html
Copyright © 2020-2023  润新知