bzoj3083

本文分享了一道关于树剖和区间更新查询的算法题目,通过树状结构进行高效的区间赋值和查询操作,详细解释了解题思路和实现细节。

写这题的时候状态贼好,想了20min就想到正解了qwq

首先链上赋值没难度吧,直接树剖后打标记区间赋值维护一下区间最小就好了

然后考虑换根问题:

(ro为当前根,x为要求的子树的根)

1.当ro==x时,求全局最小

2.当lca(ro,x)!=x时,说明此时根在x的子树外,莫得影响

3.当lca(ro,x)==x时,说明此时根在x的子树内,倍增将ro将跳到x的son上,然后就查询出除son的子树外的最小

/**************************************************************

    Problem: 3083

    User: syh0313

    Language: C++

    Result: Accepted

    Time:4996 ms

    Memory:22800 kb

****************************************************************/

 

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <string>

#include <algorithm>

#include <cmath>

#define lch a[n].lc

#define rch a[n].rc

using namespace std;

const long long inf=100000000000;

const int maxn=100010;

int n,m,root,ro,xx,topt,cnt;

struct da{int lc,rc;long long mi,tag;}a[maxn*4];

int st[maxn<<1],nt[maxn<<1],to[maxn<<1];

int dep[maxn],fa[maxn],rem[maxn],si[maxn];

int top[maxn],dfn[maxn],dfn_num,line[maxn];

bool f[maxn];

long long v[maxn];

void add(int x,int y)

{to[++topt]=y; nt[topt]=st[x]; st[x]=topt;}

void dfs1(int x,int de)

{

    f[x]=1; dep[x]=de; si[x]=1; int p=st[x],ma=0;

    while (p)

    {

        if (!f[to[p]])

        {

            dfs1(to[p],de+1);

            si[x]+=si[to[p]];

            fa[to[p]]=x;

            if (si[to[p]]>ma) {ma=si[to[p]]; rem[x]=to[p];}

        }

        p=nt[p];

    }

}

void dfs2(int x)

{

    f[x]=1; if (rem[fa[x]]==x) top[x]=top[fa[x]];else top[x]=x;

    dfn[x]=++dfn_num; line[dfn_num]=x; int p=st[x];

    if (rem[x]) dfs2(rem[x]);

    while (p)

    {

        if (!f[to[p]]) dfs2(to[p]);

        p=nt[p];

    }

}

void updata(int n){a[n].mi=min(a[lch].mi,a[rch].mi);}

void build_tree(int &n,int l,int r)

{

    n=++cnt; if (l==r) {a[n].mi=v[line[l]]; return;}

    int mid=(l+r)>>1;

    build_tree(lch,l,mid); build_tree(rch,mid+1,r);

    updata(n);

}

void pushdown(int n)

{

    if (!a[n].tag) return;

    a[lch].mi=a[rch].mi=a[n].tag;

    a[lch].tag=a[rch].tag=a[n].tag;

    a[n].tag=0;

}

void tree_fz(int n,int L,int R,int l,int r,long long k)

{

    if (L==l && R==r) {a[n].mi=k; a[n].tag=k; return;}

    int mid=(L+R)>>1; pushdown(n);

    if (r<=mid) tree_fz(lch,L,mid,l,r,k);

    else if (l>=mid+1) tree_fz(rch,mid+1,R,l,r,k);

    else tree_fz(lch,L,mid,l,mid,k),tree_fz(rch,mid+1,R,mid+1,r,k);

    updata(n);

}

void qfz(int x,int y,long long k)

{

    while (top[x]!=top[y])

    {

        if (dep[top[x]]<dep[top[y]]) swap(x,y);

        tree_fz(root,1,n,dfn[top[x]],dfn[x],k);

        x=fa[top[x]];

    }

    if (dfn[x]>dfn[y]) swap(x,y);

    tree_fz(root,1,n,dfn[x],dfn[y],k);

}

long long qury(int n,int L,int R,int l,int r)

{

    if (l>r) return inf;

    if (L==l && R==r) return a[n].mi;

    int mid=(L+R)>>1; pushdown(n);

    if (r<=mid) return qury(lch,L,mid,l,r);

    else if (l>=mid+1) return qury(rch,mid+1,R,l,r);

    else return min(qury(lch,L,mid,l,mid),qury(rch,mid+1,R,mid+1,r));

    updata(n);

}

int lca(int x,int y)

{

    while (top[x]!=top[y])

    {

        if (dep[top[x]]<dep[top[y]]) swap(x,y);

        x=fa[top[x]];

    }

    if (dep[x]<dep[y]) return x;

return y;

}

int find(int x,int y)

{

    while (fa[x]!=y)

    {

        if (dep[fa[top[x]]]>dep[y]) x=fa[top[x]];

        else if (dep[fa[top[x]]]==dep[y]) x=top[x];

        else x=rem[y];

    }

return x;

}

int main()

{

    scanf("%d%d",&n,&m);

    for (int i=1;i<n;i++)

    {

        int xx,yy; scanf("%d%d",&xx,&yy);

        add(xx,yy); add(yy,xx);

    }

    for (int i=1;i<=n;i++) scanf("%lld",&v[i]);

    dfs1(1,1); memset(f,0,sizeof f); dfs2(1);

    build_tree(root,1,n);

    scanf("%d",&ro);

    while (m--)

    {

        int ff=0; scanf("%d",&ff);

        if (ff==1) {scanf("%d",&xx); ro=xx;}

        else if (ff==2)

        {

            int p1,p2; long long vv; scanf("%d%d%lld",&p1,&p2,&vv);

            qfz(p1,p2,vv);

        }

        else if (ff==3)

        {

            scanf("%d",&xx); int lc=lca(ro,xx);

            if (ro==xx) printf("%lld\n",a[root].mi);

            else if (lc!=xx) printf("%lld\n",qury(root,1,n,dfn[xx],dfn[xx]+si[xx]-1));

            else

            {

                int xxx=find(ro,xx);

                printf("%lld\n",min(qury(root,1,n,1,dfn[xxx]-1),qury(root,1,n,dfn[xxx]+si[xxx],n)));

            }

        }

    }

return 0;

}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值