[bzoj3083][树链剖分]遥远的国度

3083: 遥远的国度

Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 4426 Solved: 1190
[Submit][Status][Discuss]
Description

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

问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。

Input

第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为根的子树中的最小防御值。

Output

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

Sample Input

3 7

1 2

1 3

1 2 3

1

3 1

2 1 1 6

3 1

2 2 2 5

3 1

2 3 3 4

3 1

Sample Output

1

2

3

4

提示

对于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。
HINT

Source

zhonghaoxi提供

sol:

考虑有换根,显然不能lct。考虑用树剖来做,分类小讨论一波,容易发现,rt=x的时候就是整个子树,x在rt到根的路径上时就是整个树去掉x到rt方向上第一个点的子树,这个在树剖上跑一下就行了,不在重链上直接跳,否则用dfs序取出重链上的某个距离上的点。然后就是把dfs序分成2个区间求解。否则就是正常的求子树。
一开始以为权值能是0,网上搞了一份代码下来拍,把tag值改成1e15,结果标记下放完tag=0,调了1h可以说是很灵性了。

#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;

inline int read()
{
    char c;
    bool pd=0;
    while((c=getchar())>'9'||c<'0')
    if(c=='-') pd=1;
    int res=c-'0';
    while((c=getchar())>='0'&&c<='9')
    res=(res<<3)+(res<<1)+c-'0';
    return pd?-res:res;
}
const int N=410000;
int dep[N],fa[N],top[N],son[N],size[N],dfn[N],rev[N];
int fir[N],go[N],nex[N],tot;
inline void add(int x,int y)
{
    nex[++tot]=fir[x];fir[x]=tot;go[tot]=y;
    nex[++tot]=fir[y];fir[y]=tot;go[tot]=x;
}
inline void dfs1(int u)
{
    dep[u]=dep[fa[u]]+1;
    size[u]=1;
    int e,v;
    for(int e=fir[u];v=go[e],e;e=nex[e])
    if(v!=fa[u])
    {
        fa[v]=u;
        dfs1(v);
        size[u]+=size[v];
        if(size[v]>size[son[u]]) son[u]=v;
    }
}
inline void dfs2(int u,int d)
{
    dfn[u]=++dfn[0];
    rev[dfn[0]]=u;
    top[u]=d;
    if(son[u]) dfs2(son[u],d);
    else return;
    int e,v;
    for(e=fir[u];v=go[e],e;e=nex[e])
    if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
typedef long long ll;
ll Min[N],tag[N],a[N];
inline void updata(int k)
{
    Min[k]=min(Min[k<<1],Min[k<<1|1]);
}
inline void build(int k,int l,int r)
{
    tag[k]=1e15;
    if(l==r)
    {
        Min[k]=a[rev[l]];
        return;
    }
    int mid=l+r>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    updata(k);
}
inline void tag_down(int k)
{
    if(tag[k]!=1e15)
    {
        Min[k<<1]=tag[k];
        Min[k<<1|1]=tag[k];
        tag[k<<1]=tag[k];
        tag[k<<1|1]=tag[k];
        tag[k]=1e15;
    }
}
inline void modify(int k,int l,int r,int L,int R,ll v)
{
    if(L<=l&&r<=R)
    {
        Min[k]=v;
        tag[k]=v;
        return;
    }
    tag_down(k);
    int mid=l+r>>1;
    if(mid>=L) modify(k<<1,l,mid,L,R,v);
    if(mid< R) modify(k<<1|1,mid+1,r,L,R,v);
    updata(k);
}
inline void query(int k,int l,int r,int L,int R,ll &res)
{
    if(L<=l&&r<=R)
    {
        res=min(res,Min[k]);
        return;
    }
    int mid=l+r>>1;
    tag_down(k);
    if(mid>=L) query(k<<1,l,mid,L,R,res);
    if(mid< R) query(k<<1|1,mid+1,r,L,R,res);
}
int n,m;
inline void modify(int x,int y,ll v)
{
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
        modify(1,1,n,dfn[fx],dfn[x],v);
        x=fa[fx];fx=top[x];
    }
    if(dep[x]<dep[y]) swap(x,y);
    modify(1,1,n,dfn[y],dfn[x],v);
}
inline int lca(int x,int y)
{
    int fx=top[x];
    while(dep[fx]>y)
    x=fa[fx],fx=top[x];
    return rev[dfn[x]-dep[x]+y];
}
int rt,Lca;
ll ans;
int main()
{
//  freopen("3083.in","r",stdin);
//  freopen("3083.out","w",stdout);
    n=read();
    m=read();
    for(int i=2;i<=n;++i) add(read(),read());
    for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
    dfs1(1);
    dfs2(1,1);
    build(1,1,n);
    rt=read();
    for(int i=1;i<=m;++i)
    {
        int tp,x,y;
        ll z;
        tp=read();
        x=read();
        if(tp==1) rt=x;
        if(tp==2)
        {
            y=read();
            scanf("%lld",&z);
            modify(x,y,z);
        }
        if(tp==3)
        {
            ans=1e15;
            if(rt==x) ans=Min[1];
            else if(dfn[x]<=dfn[rt]&&dfn[rt]<=dfn[x]+size[x]-1)
            {
                Lca=lca(rt,dep[x]+1);
                if(Lca!=1) query(1,1,n,1,dfn[Lca]-1,ans);
                if(dfn[Lca]+size[Lca]<=n) query(1,1,n,dfn[Lca]+size[Lca],n,ans);
            }
            else query(1,1,n,dfn[x],dfn[x]+size[x]-1,ans);
            printf("%lld\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值