洛谷P3979 遥远的国度【树链剖分】

时空限制 1000ms / 128MB

题目描述

zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度。当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀。

问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。

RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。

由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。

输入格式:

第1行两个整数n m,代表城市个数和操作数。
第2行至第n行,每行两个整数 u v,代表城市u和城市v之间有一条路。
第n+1行,有n个整数,代表所有点的初始防御值。
第n+2行一个整数 id,代表初始的首都为id。
第n+3行至第n+m+2行,首先有一个整数opt,如果opt=1,接下来有一个整数id,代表把首都修改为id;如果opt=2,接下来有三个整数p1 p2 v,代表将p1 p2路径上的所有城市的防御值修改为v;如果opt=3,接下来有一个整数 id,代表询问以城市id为根的子树中的最小防御值。

输出格式:

对于每个opt=3的操作,输出一行代表对应子树的最小点权值。

说明

对于20%的数据,n<=1000 m<=1000。
对于另外10%的数据,n<=100000,m<=100000,保证修改为单点修改。
对于另外10%的数据,n<=100000,m<=100000,保证树为一条链。
对于另外10%的数据,n<=100000,m<=100000,没有修改首都的操作。
对于100%的数据,n<=100000,m<=100000,0<所有权值<=2^31


题目分析

首先路径修改与根无关,所以直接按套路操作
假设一开始树剖的根为1,当前根为rt,询问点为u
1.若u==rt,u得子树就是整棵树
2.若LCA(u,rt)!=u,则u的子树不变
3.若LCA(u,rt)==u,则u的子树(以rt为根)为 整棵树 - rt的祖先中u的儿子的子树(以1为根)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
#include<cstring>
#include<iomanip>
using namespace std;
typedef long long lt;
typedef double dd;

lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const lt inf=1e10;
const int maxn=200010;
int n,m,rt;
lt a[maxn];
struct node{int v,nxt;}E[maxn<<1];
int head[maxn],tot;
int top[maxn],son[maxn],fa[maxn],size[maxn];
int dep[maxn],num[maxn],pos[maxn],cnt;
lt mi[maxn<<2],sett[maxn<<2];

void add(int u,int v)
{
    E[++tot].nxt=head[u];
    E[tot].v=v;
    head[u]=tot;
}

void dfs1(int u,int pa)
{
    size[u]=1;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(v==pa) continue;
        dep[v]=dep[u]+1;  fa[v]=u;
        dfs1(v,u);
        size[u]+=size[v];
        if(size[v]>size[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int tp)
{
    num[u]=++cnt; pos[cnt]=u; top[u]=tp;
    if(son[u]) dfs2(son[u],tp);
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void build(int s,int t,int p)
{
    sett[p]=-1;
    if(s==t){ mi[p]=a[pos[s]]; return;}
    int mid=s+t>>1;
    build(s,mid,p<<1);build(mid+1,t,p<<1|1);
    mi[p]=min(mi[p<<1],mi[p<<1|1]);
}

void push(int p)
{
    if(sett[p]==-1) return;
    mi[p<<1]=mi[p<<1|1]=sett[p];
    sett[p<<1]=sett[p<<1|1]=sett[p];
    sett[p]=-1;
}

void update(int ll,int rr,int s,int t,int p,lt val)
{
    if(ll<=s&&t<=rr){ mi[p]=sett[p]=val; return;}
    int mid=s+t>>1; push(p);
    if(ll<=mid) update(ll,rr,s,mid,p<<1,val);
    if(rr>mid) update(ll,rr,mid+1,t,p<<1|1,val);
    mi[p]=min(mi[p<<1],mi[p<<1|1]);
}

void modify(int u,int v,lt val)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        update(num[top[u]],num[u],1,n,1,val);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    update(num[u],num[v],1,n,1,val);
}

lt qmin(int ll,int rr,int s,int t,int p)
{
    if(ll<=s&&t<=rr) return mi[p];
    int mid=s+t>>1;lt res=inf; push(p);
    if(ll<=mid) res=min(res,qmin(ll,rr,s,mid,p<<1));
    if(rr>mid) res=min(res,qmin(ll,rr,mid+1,t,p<<1|1));
    return res;
}

int LCA(int u,int v)
{
    while(top[u]!=top[v])
    {
    	if(dep[top[u]]>dep[top[v]]) u=fa[top[u]];
    	else v=fa[top[v]];
    }
    return dep[u]<dep[v]?u:v;
}

int find(int u,int v)
{
    while(top[v]!=top[u]){
    	v=top[v];
        if(fa[v]==u) return v;
    	v=fa[v];
   	}
    return son[u];
}

int query(int u)
{
    if(u==rt) return mi[1];
    int lca=LCA(u,rt);
    if(lca==u){
    	int ch=find(u,rt);
        int ll=num[ch]-1,rr=num[ch]+size[ch];
        return min(qmin(1,ll,1,n,1),rr<=n?qmin(rr,n,1,n,1):inf);
    }
    else return qmin(num[u],num[u]+size[u]-1,1,n,1);
}

int main()
{
    n=read();m=read();
    for(int i=1;i<n;++i)
    {
        int u=read(),v=read();
        add(u,v); add(v,u);
    }
    for(int i=1;i<=n;++i) a[i]=read();
    rt=read();
    
    dep[1]=1; dfs1(1,0); dfs2(1,1); 
    build(1,n,1);
    
    while(m--)
    {
        int opt=read();
        if(opt==1) rt=read();
        else if(opt==2)
        {
            int u=read(),v=read();lt val=read();
            modify(u,v,val);
        }
        else if(opt==3)
        {
            int u=read();
            printf("%lld\n",query(u));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值