P3979 遥远的国度

P3979 遥远的国度

长者的题qwq

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

53knIz.png

一个最普通的询问

53kRT2.png

如果我们换一次根

就会变成一下情况

53l5gN.png

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

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

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

53k2fQ.png

就像上面一样

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

53kgJa.png]

看起来很简单的样子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;
        }
    }
}

转载于:https://www.cnblogs.com/Lance1ot/p/9390577.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值