zoukankan      html  css  js  c++  java
  • [BZOJ 3083] 遥远的国度

    Link:https://www.lydsy.com/JudgeOnline/problem.php?id=3083

    Solution:

    算是一道树剖的模板题吧

    除了模板以外,新增的操作就是“换$root$”

    此时我们分类讨论即可:

    1、$x={root}$   查询$[1,n]$   (需要特判

    2、$x ot={lca(x,root)}$   直接查询子树即可,不受$root$变化的影响

    3、$x={lca(x,root)}$   发现现在x的子树就是整棵树减去$x$往$root$方向向下最近的那个节点的子树,查询其补集即可

    dfs序:

    也是套路了,由于子树中dfs序连续,用dfs序整体维护子树($root$为$x$的子树范围为$[L[x],R[x]]$)

    若$x$往$root$方向最近的点为$v$,则除去其子树的补集为$[1,L[v]-1]+[R[v]+1,n]$

    Code:

    //by NewErA
    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF=2147483647;
    
    #define mid (l+r)/2
    #define lc k<<1,l,mid
    #define rc k<<1|1,mid+1,r
    
    const int MAXN=1e5+5;
    int m,n,root=0,siz[MAXN],dep[MAXN],top[MAXN],f[MAXN][20],pos[MAXN],dat[MAXN],L[MAXN],R[MAXN],cnt=0;
    int tag[5*MAXN],mn[5*MAXN],w[MAXN];
    vector<int> a[MAXN];
    
    void dfs1(int k)
    {
        siz[k]=1;
        for(int i=1;(1<<i)<=dep[k];i++)  //最好将f在dfs1就更新掉,不能在处理完叶子结点后再更新
            f[k][i]=f[f[k][i-1]][i-1];    
        
        for(int i=0;i<a[k].size();i++)
        {
            if(a[k][i]==f[k][0]) continue;
            f[a[k][i]][0]=k;
            dep[a[k][i]]=dep[k]+1;
            
            dfs1(a[k][i]);
            
            siz[k]+=siz[a[k][i]];
        }
    }
    
    void dfs2(int k,int up)
    {
        int bs=0;
        pos[k]=L[k]=++cnt,top[k]=up;
        for(int i=0;i<a[k].size();i++)
            if(a[k][i]!=f[k][0] && siz[a[k][i]]>siz[bs])
                bs=a[k][i];
        if(!bs){R[k]=L[k];return;}
        
        dfs2(bs,up);
        
        for(int i=0;i<a[k].size();i++)
            if(a[k][i]!=f[k][0] && a[k][i]!=bs)
                dfs2(a[k][i],a[k][i]);
        R[k]=cnt;
    }
    
    void push_up(int k){mn[k]=min(mn[k<<1],mn[k<<1|1]);}
    
    void push_down(int k)
    {
        if(tag[k])
        {
            tag[k<<1]=tag[k<<1|1]=tag[k];
            mn[k<<1]=mn[k<<1|1]=tag[k];
            tag[k]=0;
        }
    }
    
    void build(int k,int l,int r)
    {
        if(l==r){mn[k]=w[l];return;}
        build(lc);build(rc);
        push_up(k);
    }
    
    void update_same(int a,int b,int x,int k,int l,int r)
    {
        if(a<=l && r<=b)
        {
            mn[k]=tag[k]=x;
            return;
        }
        push_down(k);
        if(a<=mid) update_same(a,b,x,lc);
        if(b>mid) update_same(a,b,x,rc);
        push_up(k);
    }
    
    int query_min(int a,int b,int k,int l,int r)
    {
        if(a<=l && r<=b) return mn[k];
        push_down(k);
        int ret=INF;
        if(a<=mid) ret=min(ret,query_min(a,b,lc));
        if(b>mid) ret=min(ret,query_min(a,b,rc));
        return ret;
    }
    
    void solve_same(int u,int v,int x)
    {
        while (top[u]!=top[v])  //这里是while……
        {
            if(dep[top[u]]<dep[top[v]]) swap(u,v);
            update_same(pos[top[u]],pos[u],x,1,1,n);
            u=f[top[u]][0];
        }
        if(pos[u]>pos[v]) swap(u,v);
        update_same(pos[u],pos[v],x,1,1,n);
    }
    
    int LCA(int u,int v)
    {
        while(top[u]!=top[v])
        {
            if(dep[top[u]]<dep[top[v]]) swap(u,v);
            u=f[top[u]][0];
        }
        if(dep[u]>dep[v]) swap(u,v);
        return u;
    }
    
    int find_nearest(int u,int depk)  //寻找x到root方向最近的节点
    {
        int temp=dep[u]-depk;
        for(int i=0;i<=17;i++)
            if(temp&(1<<i)) u=f[u][i];
        return u;
    }
    
    int solve_max(int k)
    {
        if(k==root) return query_min(1,n,1,1,n);  //特判
        else
        {
            int lca=LCA(k,root);
            if(lca!=k) return query_min(L[k],R[k],1,1,n);  //直接查询子树
            else
            {
                int nst=find_nearest(root,dep[k]+1);
                return min(query_min(1,L[nst]-1,1,1,n),query_min(R[nst]+1,n,1,1,n));
            }
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++)
        {
            int x,y;scanf("%d%d",&x,&y);
            a[x].push_back(y);a[y].push_back(x);
        }
        for(int i=1;i<=n;i++) scanf("%d",&dat[i]);
        scanf("%d",&root);
        
        dfs1(root);dfs2(root,root);
        for(int i=1;i<=n;i++) w[pos[i]]=dat[i];
        build(1,1,n);
            
        for(int i=1;i<=m;i++)
        {
            int op,x,y,z;scanf("%d%d",&op,&x);
            if(op==1) root=x;
            else if(op==2) scanf("%d%d",&y,&z),solve_same(x,y,z);
            else printf("%d
    ",solve_max(x));
        }
        return 0;
    }

    Review:

    1、整体维护子树,想到利用dfs序

    2、犯的丝帛错误:

    (1)将更新f数组的循环放到dfs2中处理完叶子结点后了,最好在dfs1中就更新掉

    (2)线段树处理中将最外层的while打成if了……

  • 相关阅读:
    .Net 更容易的使用配置文件 SuperConfig
    .Net 5分钟搞定网页实时监控
    记一次sql server 性能调优,查询从20秒至2秒
    [asp.net mvc 奇淫巧技] 05
    [asp.net mvc 奇淫巧技] 04
    [asp.net mvc 奇淫巧技] 03
    [asp.net mvc 奇淫巧技] 02
    ECharts 实现人民的名义关系图谱 代码开源
    C# 快速高效率复制对象另一种方式 表达式树
    Asp.net SignalR 应用并实现群聊功能 开源代码
  • 原文地址:https://www.cnblogs.com/newera/p/9108295.html
Copyright © 2011-2022 走看看