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\n",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\n",tree_query(b));
            continue;
        }
    }
}
posted @ 2018-07-30 14:58  Lance1ot  阅读(201)  评论(0编辑  收藏  举报