• 虚树套餐(随缘更新)


    由于本人太菜,所以字符串专题只学会了虚树一个知识点,所以写文乱giao

    首先假装大佬地讲一下虚树:

    虚树主要用于优化树DP,由于有些题hin贱,会有多次查询修改,还只查询一些关键点,此时直接树DP就跑不过去了。

    这时我们想到,能不能减少点的个数,使树DP能跑得飞快呢?

    学的时候联想到了这题,说不定也能用虚树做

    我们通过把一条链压缩成一条边来简化这棵树

    比如这棵树,黑色点是关键点,简化后就变成了

    这时我们懒得遍历2 3 6 4这些节点了,反正也没用(-_-)

    但是7号节点还是有必要留,毕竟还要递归嘛~~

    但怎么找到所有要保留的点并连边呢

    维护右链

    我们开一个栈,记录当前“最靠右的链”

    先连接1号节点和最靠左的关键节点 8号节点

     

    (红色是关键点,蓝色是右链,节点3 7都被压成了一条边) 

    (是的,右链一开始是在左边的)

    然后我们要让右链不断“往右移”

    我们于是把更加“靠右”(是的,就是 物 理 靠 右 )的9号点添加进来,但是我们不能“把一个点加到一条边上”啊

    我萌就把右链底端的8号点和新加入的9号点的lca:7号点还原,从而加入9

    重复以上操作就可以构建出虚树

    什么,你问怎么找到“物理靠右”?肯定是dfs序啦.

    void build_vtree(ll x){
        if(tt==1){
            st[++tt]=x;
            return ;
        }
        ll lca=L_C_A(st[tt],x);
        while(tt>1&&dfn[st[tt-1]]>=dfn[lca]){
            add2(st[tt-1],st[tt]);
            tt--;
        }
        if(lca!=st[tt]){
            add2(lca,st[tt]);
            st[tt]=lca;
        }
        st[++tt]=x;
    }
    
    int main(){
        .............
        while(tt>1){
            add2(st[tt-1],st[tt]);
            tt--;
        } 
        .............
    }

    st是我们用来记录右链的栈(tt是top),需要注意的是,我们不是直接把st里的点都连接上

    而是在退栈时才连接,这样就能把lca们都连上了

    例题

    消耗战

    题意:ZEZ是一位高级玩家,他在一款战略游戏中把游戏蒟蒻FJH逼入了墙角,现在ZEZ胜利在望,FJH只剩下一座碉堡,ZEZ需要切断一些碉堡与外界的通道,让DFH无法获得食物,就能击败他

    但是FJH是一位玄学高手,他的粮食生产基地的位置会随着时间变化而变化,所以你需要每次都切断他的粮食来源,才能保证ZEZ的胜利(详细请看洛谷题面)

    先考虑下暴力树DP

    我们只要在经过每个节点x的时候,考虑切掉x子树中一些的边,来切断关键点与根节点的联系(孤立掉关键点)

    然后虚树优化看代码就好  

    需要注意直接memset必超时

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define N 250010
    using namespace std;
    ll hh[N],head[N],dfn[N],f[30][N],dep[N],st[N],dis[N],mn[N];
    ll n,m,cnt,ret,top,tt;
    
    struct The_World{
        ll to,nxt,w;
    }q[N*10];
    
    struct Star_Platinum{
        ll v,nx;
    }e[N*10];
    
    struct key_point{
        ll num,df;
        bool operator <(const key_point&tmp )const{
            return df<tmp.df;
        }
    }a[N];
    
    void add(ll u,ll v,ll w){
        q[++cnt].to=v;
        q[cnt].nxt=head[u];
        q[cnt].w=w;
        head[u]=cnt;
    }
    
    void add2(ll u,ll v){
        e[++ret].v=v;
        e[ret].nx=hh[u];
        hh[u]=ret;
    }
    
    void dfs(ll x,ll fa){
        dep[x]=dep[fa]+1;
        dfn[x]=++top;
        for(int i=1;i<=19&&f[i-1][x];i++){
            f[i][x]=f[i-1][f[i-1][x]];
        }
        for(ll i=head[x];i;i=q[i].nxt){
            ll v=q[i].to;
            if(v!=fa){
                ll v=q[i].to;
                if(!dfn[v]){
                    f[0][v]=x;
                    mn[v]=min(mn[x],q[i].w);
                    dfs(v,x);
                }
            }
        }
    }
    
    ll LYCA(ll x,ll y){
        if(dep[y]>dep[x])swap(x,y);
        for(int i=20;i>=0;i--){
            if(dep[f[i][x]]>=dep[y]){
                x=f[i][x];
            }
            if(x==y)return x;
        }
        for(int i=20;i>=0;i--){
            if(f[i][x]!=f[i][y]){
                x=f[i][x];
                y=f[i][y];
            }
        }
        return f[0][x];
    }
    
    void make_vtree(ll x){
        if(tt==1){
            st[++tt]=x;return ;
        }
        ll lca=LYCA(st[tt],x);
        if(lca==st[tt])return ;
        while(tt>1&&dfn[st[tt-1]]>=dfn[lca]){
            add2(st[tt-1],st[tt]);
            tt--;
        }
        if(lca!=st[tt]){
            add2(lca,st[tt]);
            st[tt]=lca;
        }
        st[++tt]=x;
    }
    
    ll DP(ll x,ll fa){
        ll sum=0;
        bool poo=0;
        for(ll i=hh[x];i;i=e[i].nx){
            ll v=e[i].v;
            if(v!=fa){
                sum+=DP(v,x);
                poo=1;
            }
        }
        hh[x]=0;
        if(sum)return min(sum,mn[x]);
        return mn[x];
    }
    
    int main(){
        mn[1]=1ll<<60;
        scanf("%lld",&n);
        for(int i=1;i<n;i++){
            ll a1,a2,a3;
            scanf("%lld%lld%lld",&a1,&a2,&a3);
            add(a1,a2,a3);
            add(a2,a1,a3);
        }
        dfs(1,0);
        scanf("%lld",&m);
        for(int i=1;i<=m;i++){
            ll a1;
            tt=0,ret=0;
            scanf("%lld",&a1);
            for(ll i=1;i<=a1;i++){
                scanf("%lld",&a[i].num);
                a[i].df=dfn[a[i].num];
            }
            sort(1+a,1+a+a1);
            st[++tt]=1;
            for(ll i=1;i<=a1;i++){
                if(a[i].num==1)continue;
                make_vtree(a[i].num);
            }
            while(tt>0){
                add2(st[tt-1],st[tt]);
                tt--;
            }
            printf("%lld
    ",DP(1,0));
        }
    }

    一道很烦的题

    世界树

    挺烦的,居然有人用了6遍DFS

    先构建虚树

    定义belx为x归属的点(就是管理x节点的那个议事处),disx为x到belx的距离

    然后我们用两次dfs,第一次向上寻找belx,第二次dfs在dp前先向下找belx

    那么怎么处理每个议事处管理的点呢,定义six为x控制的节点数量,si初始化为sizx

    那么定义bau(u为议事处)为x控制的种族个数,那么bau=Σsix(x为被u控制的关键点(不要忘记我们建了虚树哦))

    当belx==belv的时候six直接减siv是吧

    但是当不等于是,就说明x->v这条链中,有些点属于belx,有些属于belv

    那么我们就倍增找出这个分割点,然后分别统计到six和siv

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define vv q[i].to
    #define v1 e[i].v
    #define N 400000
    using namespace std;
    ll head[N],hh[N],dfn[N],f[30][N],st[N],dep[N],dis[N],bel[N],siz[N],ba[N],si[N],tr[N];
    ll n,m,qu,cnt,ret,top,tt,tpp;
    bool pool[N];
    /////////需要注意v1是虚树的v vv是原树的v 
    
    struct key_point{
        ll num,ii,df;
    }a[N];
    
    struct The_World{
        ll to,nxt;
    }q[N+10];
    
    struct Star_Platinum{
        ll nx,v,w;
    }e[N+10];
    
    bool cmp1(key_point a,key_point b){return a.df<b.df;}
    
    bool cmp2(key_point a,key_point b){return a.ii<b.ii;}
    
    void add1(ll u,ll v){
        q[++cnt].to=v,q[cnt].nxt=head[u],head[u]=cnt;
        q[++cnt].to=u,q[cnt].nxt=head[v],head[v]=cnt;
    }
    
    void add2(ll u,ll v,ll w){
        e[++ret].v=v,e[ret].nx=hh[u],e[ret].w=w,hh[u]=ret;
        e[++ret].v=u,e[ret].nx=hh[v],e[ret].w=w,hh[v]=ret;
    }
    
    void dfs(ll x,ll fa){
        dfn[x]=++top;
        dep[x]=dep[fa]+1;
        siz[x]=1;
        for(ll i=1;i<=20&&f[i-1][x];i++){
            f[i][x]=f[i-1][f[i-1][x]];
        }
        for(ll i=head[x];i;i=q[i].nxt){
            if(vv!=fa){
                if(!dfn[vv]){
                    f[0][vv]=x;
                    dfs(vv,x);
                    siz[x]+=siz[vv];
                }
            }
        }
    }
    
    ll L_Y_C_A(ll x,ll y){
        if(dep[y]>dep[x])swap(x,y);
        for(ll i=20;i>=0;i--){
            if(dep[f[i][x]]>=dep[y]){
                x=f[i][x];
            }
            if(x==y)return x;
        } 
        for(ll i=20;i>=0;i--){
            if(f[i][x]!=f[i][y]){
                x=f[i][x];
                y=f[i][y];
            }
        }
        return f[0][x];
    }
    
    void build_vtree(ll x){
        if(tt==1){
            st[++tt]=x;
            return ;
        }
        ll lca=L_Y_C_A(st[tt],x);
        while(tt>1&&dfn[st[tt-1]]>=dfn[lca]){
            add2(st[tt-1],st[tt],dep[st[tt]]-dep[st[tt-1]]);
            tt--;
        }
        if(lca!=st[tt]){
            add2(lca,st[tt],dep[st[tt]]-dep[lca]);
            st[tt]=lca;
        }
        st[++tt]=x;
    }
    
    ll CA(ll x,ll kk){
        if(dep[x]<kk)return x;
        for(ll i=20;i>=0;i--){
            if(dep[f[i][x]]>=kk)x=f[i][x];
        }
        return x;
    }
    
    void DP1(ll x,ll fa){
        tr[++tpp]=x;
        si[x]=siz[x];
        if(pool[x]){
            dis[x]=0,bel[x]=x;
        }
        else dis[x]=1e9;
        for(ll i=hh[x];~i;i=e[i].nx){
            if(v1!=fa){
                DP1(v1,x);
                if(dis[x]>dis[v1]+e[i].w||(dis[x]==dis[v1]+e[i].w&&bel[x]>bel[v1])){
                    bel[x]=bel[v1];
                    dis[x]=dis[v1]+e[i].w;
                }
            }
        }
    }
    
    void DP2(ll x,ll fa){
        for(ll i=hh[x];~i;i=e[i].nx){
            if(fa!=v1){
                if(dis[v1]>dis[x]+e[i].w||(dis[x]+e[i].w==dis[v1]&&bel[v1]>bel[x])){
                    dis[v1]=dis[x]+e[i].w;
                    bel[v1]=bel[x];
                }
                DP2(v1,x);
                if(bel[x]==bel[v1]){
                    si[x]-=siz[v1];
                }
                else {
                    ll d=dis[v1]+dis[x]+dep[v1]-dep[x]-1,k;//向下取整
                    k=d/2-dis[v1];
                    ll tmp=CA(v1,dep[v1]-k);
                    if((d&1)&&bel[x]>bel[v1]&&k>=0)tmp=f[0][tmp];
                    si[v1]+=siz[tmp]-siz[v1];
                    si[x]-=siz[tmp]; 
                }
                ba[bel[v1]]+=si[v1];
            }
        }
        if(x==1)ba[bel[x]]+=si[x];
    }
    
    int main(){
        scanf("%lld",&n);
        for(ll i=1;i<n;i++){
            ll a1,a2;
            scanf("%lld%lld",&a1,&a2);
            add1(a1,a2);
        }
        dfs(1,0);
        memset(hh,-1,sizeof hh);
        scanf("%lld",&m);
        while(m--){
            ret=0;
            ll a1;
            scanf("%lld",&a1);
            for(ll i=1;i<=a1;i++){
                scanf("%lld",&a[i].num);
                a[i].ii=i,a[i].df=dfn[a[i].num];
                pool[a[i].num]=1;
            }
            sort(1+a,1+a+a1,cmp1);
            tt=1;st[tt]=1;
            for(ll i=1;i<=a1;i++){
                if(a[i].num==1)continue;
                build_vtree(a[i].num);
            }
            while(tt>1){
                add2(st[tt-1],st[tt],dep[st[tt]]-dep[st[tt-1]]);
                tt--;
            }
            DP1(1,0);
            DP2(1,0);
            sort(1+a,1+a+a1,cmp2);
            for(ll i=1;i<=a1;i++){
                printf("%lld ",ba[a[i].num]);
            }
            putchar('
    ');
            for(int i=1;i<=tpp;i++){
                pool[tr[i]]=0;
                hh[tr[i]]=-1;
                si[tr[i]]=dis[tr[i]]=bel[tr[i]]=ba[tr[i]]=0;
            }
            tpp=0;
        }
    }
  • 相关阅读:
    Pagination 分页类
    FckEditorAPI未定义错误分析
    提取DataSet数据集中前N条记录
    JS操作JSON[转]
    JS运行textarea内的HTML代码 [转]
    使用Global.asax文件统计网站总访问量
    文章点击数简单实现周、月、年排行
    asp.net文件下载[转]
    三大策略保证论坛不受垃圾信息影响![转]
    图片以二进制形式写入数据库并显示
  • 原文地址:https://www.cnblogs.com/caijiLYC/p/13376401.html
Copyright © 2020-2023  润新知