【BZOJ3083】遥远的国度,树链剖分练习

传送门
写在前面:链剖裸题里的战斗机
思路:Drusher向我推荐了这道污题,据说他请Claris调了一周才A,于是我怀着忐忑的心情开始看题,其他操作都好,一个换根把我搞懵逼了,“套splay吗?”显然编程复杂度和时间复杂度都是极大的,每次换根又要重新剖分。后来看到了Lcomyn学长的讲解才大体明白,这里大家可以直接点开链接去看一下,但是我在调试第三种情况时出了一些问题,因为我用的是dfs序RMQ求lca,所以倍增求点时会出现一些奇怪的错误,20组数据WA了4组,无法只能借鉴zyf的暴力枚举法,通过记录下的搜索顺序判断x的儿子是否为root的祖先,若是则一定是我们要求的v
注意:小心数组越界,还有就是尽量不要用RMQ算法……

#include<bits/stdc++.h>
#define Inf 0x7fffffff
using namespace std;
int n,m,opt,cnt,tot,root,x,y,z;
int pos[100010],num[400010],pre[100010],top[100010],fa[100010],son[100010],L[100010],R[100010],dep[100010],siz[100010],def[100010],first[100010];
int f[400010][20];
struct os
{
    int u,v,next;
}e[200010];
struct node
{
    int minn,lazy;
}tree[400010];
void add(int x,int y)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].next=first[x];
    first[x]=tot;
}
void pushdown(int now)
{
    if (!tree[now].lazy) return;
    tree[now<<1].lazy=tree[now<<1].minn=tree[now].lazy;
    tree[now<<1|1].lazy=tree[now<<1|1].minn=tree[now].lazy;
    tree[now].lazy=0;
}
void dfs1(int now)
{
    num[++num[0]]=now;
    pos[now]=num[0];
    siz[now]=1;
    for (int i=first[now];i;i=e[i].next)
    if (e[i].v!=fa[now])
    {
        dep[e[i].v]=dep[now]+1;
        fa[e[i].v]=now;
        dfs1(e[i].v);
        num[++num[0]]=now;
        if (siz[e[i].v]>siz[son[now]]) son[now]=e[i].v;
        siz[now]+=siz[e[i].v];
    }
}
void dfs2(int now,int tp)
{
    top[now]=tp;
    L[now]=++cnt;
    pre[cnt]=now;
    if (son[now]) dfs2(son[now],tp);
    for (int i=first[now];i;i=e[i].next)
    if (e[i].v!=son[now]&&e[i].v!=fa[now])
    dfs2(e[i].v,e[i].v);
    R[now]=cnt;
}
void init()//RMQ预处理
{
    int x,y;
    for (int i=1;i<=num[0];i++) f[i][0]=num[i];
    for (int i=1;i<=log2(num[0]);i++)
    for (int j=1;j<=num[0]-(1<<i);j++)
    {
        x=f[j][i-1];y=f[j+(1<<(i-1))][i-1];
        if (dep[x]>dep[y]) f[j][i]=y;
        else f[j][i]=x;
    }
}
void build(int now,int begin,int end)
{
    if (begin==end){tree[now].minn=def[pre[end]];return;}
    int mid=(begin+end)>>1;
    build(now<<1,begin,mid);
    build(now<<1|1,mid+1,end);
    tree[now].minn=min(tree[now<<1].minn,tree[now<<1|1].minn);
}
void update(int now,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) {tree[now].minn=tree[now].lazy=z;return;}
    pushdown(now);
    int mid=(begin+end)>>1;
    if (mid>=l) update(now<<1,begin,mid,l,r);
    if (mid<r) update(now<<1|1,mid+1,end,l,r);
    tree[now].minn=min(tree[now<<1].minn,tree[now<<1|1].minn);
}
int get(int now,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return tree[now].minn;
    pushdown(now);
    int mid=(begin+end)>>1,ans=Inf;
    if (mid>=l) ans=min(ans,get(now<<1,begin,mid,l,r));
    if (mid<r) ans=min(ans,get(now<<1|1,mid+1,end,l,r));
    tree[now].minn=min(tree[now<<1].minn,tree[now<<1|1].minn);
    return ans;
}
int lca(int x,int y)
{
    int pos1=pos[x],pos2=pos[y];
    if (pos1>pos2) swap(pos1,pos2);
    int mid=log2(pos2-pos1+1);
    x=f[pos1][mid];y=f[pos2-(1<<mid)+1][mid];
    if (dep[y]>dep[x]) return x;
    else return y;
}
void solve(int l,int r)
{
    int f1=top[l],f2=top[r];
    while (f1!=f2)
    {
        if (dep[f1]<dep[f2]) swap(l,r),swap(f1,f2);
        update(1,1,cnt,L[f1],L[l]);
        l=fa[f1];f1=top[l];
    }
    if (dep[l]>dep[r]) swap(l,r);
    update(1,1,cnt,L[l],L[r]);
}
main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++)
    scanf("%d%d",&x,&y),
    add(x,y),add(y,x);
    for (int i=1;i<=n;i++) scanf("%d",&def[i]);
    scanf("%d",&root);  
    dfs1(1);
    dfs2(1,1);
    init();
    build(1,1,cnt);
    while (m--)
    {
        scanf("%d",&opt);
        if (opt==1) scanf("%d",&root);
        else if (opt==2) scanf("%d%d%d",&x,&y,&z),solve(x,y);
        else
        {
            scanf("%d",&x);
            if (x==root) printf("%d\n",tree[1].minn);
            else if (lca(x,root)!=x) printf("%d\n",get(1,1,cnt,L[x],R[x]));
            else
            {
                int ans=Inf;
                for (int i=first[x];i;i=e[i].next)
                if(L[e[i].v]<=L[root]&&R[e[i].v]>=R[root])
                {
                    z=e[i].v;
                    break;
                }
                if (L[z]>1) ans=min(ans,get(1,1,cnt,1,L[z]-1));
                if (R[z]<n) ans=min(ans,get(1,1,cnt,R[z]+1,n));
                printf("%d\n",ans);
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值