• 【loj6184】无心行挽(虚树+倍增)


    题目链接:https://loj.ac/problem/6184
    每次询问给一些关键点,询问树上每个点离最近的关键点的距离(以后称为f(u))最大值是多少。
    询问数比较大,但 sum{K} 和n是一个级别的,我们考虑每次把询问的点建成虚树,在虚树上统计答案。那些不在虚树上的点的一定是通过虚树上的点走到的,它们的f(u)也都是通过虚树上的信息来维护的。
    (update 7.17)建虚树的过程具体就是在栈中保存一条还未进行连边构图的树链,先按dfn序从小到大排序,如果新加入的点恰好是栈顶的后辈就直接把它加入栈中,如果不是就记他们的lca为u,我们只需要找到u从下到上第一个在栈中出现的祖先,并让它在top-1的位置,其他弹掉并建边即可。这是因为按dfn排过序,这时u->原栈顶 这条树链不可能再有新的分叉了,所以我们没必要保存它们,直接建边弹出栈就好了,而u以上的树链并不确定,我们还不能弹掉。至于弹后的栈顶,我们根据它是否是u来判断是否将u加入栈。做完上述操作后再将新节点加进去就没毛病了。

    细节较多的分类讨论:

    1、一个虚树上的节点的其他不在虚树中的子树中的答案,由于要排除掉在虚树上(或虚树边上)的儿子,需要预处理的时候对每个点开个有序的vector记录每个儿子子树最深值,查询时找到第一个不在虚树(边)上的儿子就break,这样统计这部分答案的均摊代价是O(虚树度数)的。
    2、一条虚树上压缩后的长度大于1的边上的点的不在虚树中的子树中的答案。这部分我们需要通过预处理的倍增数组O(log(n))地来找到从上面的节点u走最优或从下面的节点v走最优的分界点k。然后d---k询问从d走到d->k中的一个点再往不包含d的子树中走的最大值,k->u询问u向下走不经过k的子树最大值。这两个东西都可以预先用倍增数组求出来,单次查询O(log2(n))。

    注意:

    1、最好强制让1是虚树的根,否则最后还得考虑虚树的根往上走再折回来的答案。
    2、别忘了答案仍可能出在虚树上,要把虚树上节点的f值取个max。
    接下来是一份AC代码:

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i,a,b) for(register int i=(a);i<=(b);++i)
    typedef long long ll;
    const int N = 200005;
    int n,Q,nn;
    struct la{
        int gr,h[N],nxt[N],to[N],w[N];
        inline void tu(int x,int y,int c=0){to[++gr]=y,nxt[gr]=h[x],h[x]=gr,w[gr]=c;}
    }R,F;
    
    int K[N],cnt,tim,dfn[N];
    int ff[N][24],m,dep[N],zhe[N][24],dwn[N][24];
    int sta[N],top,f[N],g[N],bit[N];
    vector<int> son[N];
    bool cmp2(const int &a,const int &b){return f[a]>f[b];}
    void dfs(int u,int fa){
        dfn[u]=++tim;dep[u]=dep[fa]+1;
        ff[u][0]=fa;
        rep(i,1,m){
            ff[u][i]=ff[ff[u][i-1]][i-1];
            if(!ff[u][i])break;
        }
        for(int i=R.h[u];i;i=R.nxt[i]){
            int d=R.to[i];
            if(d==fa)continue;
            dfs(d,u);
            if(f[d]+1>f[u])g[u]=f[u],f[u]=f[d]+1;
            else g[u]=max(g[u],f[d]+1);
            son[u].push_back(d);
        }
        if(son[u].size()>0)sort(son[u].begin(),son[u].end(),cmp2);
        for(int i=R.h[u];i;i=R.nxt[i]){
            int d=R.to[i];
            if(d==fa)continue;
            zhe[d][0]=(son[u][0]==d?g[u]:f[u])+1;
            dwn[d][0]=(son[u][0]==d?g[u]:f[u]);
        }
        
    }
    void beizeng(){
        rep(i,1,m){
            rep(u,1,n){
                if(!ff[u][i])continue;
                zhe[u][i]=max(zhe[ff[u][i-1]][i-1]+bit[i-1],zhe[u][i-1]);
                dwn[u][i]=max(dwn[ff[u][i-1]][i-1],dwn[u][i-1]+bit[i-1]);
            }
        }
    }
    inline int lca(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        for(int i=m;i>=0;--i)if(dep[ff[x][i]]>=dep[y])x=ff[x][i];
        if(x==y)return x;
        for(int i=m;i>=0;--i)if(ff[x][i]!=ff[y][i])x=ff[x][i],y=ff[y][i];
        return ff[x][0];
    }
    bool cmp(int x,int y){return dfn[x]<dfn[y];}
    int dis[N],ans;
    bool is[N],in[N];
    void dp_up(int u){
        if(is[u])dis[u]=0;
        else dis[u]=0x3f3f3f3f;
        for(int i=F.h[u];i;i=F.nxt[i]){
            int d=F.to[i];
            dp_up(d);
            dis[u]=min(dis[u],dis[d]+F.w[i]);
        }
    }
    int btw(int u,int d){
        for(int i=0;i<=m;++i)if(d&bit[i])u=ff[u][i];
        return u;
    }
    void get_up(int u,int d,int k){
        if(!d)return;
        int t=0;
        for(int i=0;i<=m;++i)if(d&bit[i]){
            
            ans=max(ans,t+k+zhe[u][i]);
            u=ff[u][i],t+=bit[i];
        }
    
    }
    void get_dwn(int u,int d,int k){
        if(d<=0)return;
        for(int i=0;i<=m;++i)if(d&bit[i]){
            d-=bit[i],ans=max(ans,k+d+dwn[u][i]);
            u=ff[u][i];
            if(!d)return;
        }
    }
    int que[N],r;
    void dp_dwn(int u){
        int tmp=0,now;ans=max(ans,dis[u]);//printf("%d %d
    ",u,dis[u]);
        for(int i=F.h[u];i;i=F.nxt[i]){
            int d=F.to[i];
            dis[d]=min(dis[d],dis[u]+F.w[i]);
            dp_dwn(d);
            tmp=(dis[u]-dis[d]+F.w[i])/2;
            if(F.w[i]>1){
                if(tmp==F.w[i])get_up(d,tmp-1,dis[d]);
                else if(tmp==0)get_dwn(d,F.w[i]-1,1+dis[u]);
                else{
                    now=btw(d,tmp);
                    get_up(d,tmp,dis[d]);
                    get_dwn(now,F.w[i]-tmp-1,dis[u]+1);
                }
                now=btw(d,F.w[i]-1);
                in[now]=1;
                que[++r]=now;
            }
        }
    }
    void get_son(int u){
        rep(i,0,(int)(son[u].size()-1)){
            if(!in[son[u][i]]){ans=max(ans,dis[u]+f[son[u][i]]+1);break;}
        }
        for(int i=F.h[u];i;i=F.nxt[i]){
            int d=F.to[i];
            get_son(d);
        }
        F.h[u]=0;
        in[u]=0;
    }
    int main(){
        freopen("inception.in","r",stdin);
        freopen("inception.out","w",stdout);
        scanf("%d%d",&n,&Q);
        m=log2(n);
        int u,v;
        rep(i,2,n)scanf("%d%d",&u,&v),R.tu(u,v),R.tu(v,u);
        bit[0]=1;
        rep(i,1,m)bit[i]=bit[i-1]<<1;
        dfs(1,0);
        beizeng();
        while(Q--){
            scanf("%d",&cnt);
            rep(i,1,cnt)scanf("%d",&K[i]),is[K[i]]=1;
            sort(K+1,K+cnt+1,cmp);
            F.gr=0;ans=0;
            sta[top=1]=1;//默认让1来做虚树的根,会省去一些麻烦
            in[1]=1;
            rep(i,1,cnt){
                if(K[i]==1)continue;
                int tmp=lca(sta[top],K[i]);
                in[K[i]]=1;
                if(tmp==sta[top]){sta[++top]=K[i];continue;}
                while(top>1&&dfn[sta[top-1]]>=dfn[tmp]){
                    F.tu(sta[top-1],sta[top],dep[sta[top]]-dep[sta[top-1]]);
                    top--;
                }
                if(sta[top]!=tmp)F.tu(tmp,sta[top],dep[sta[top]]-dep[tmp]),sta[top]=tmp,in[tmp]=1;
                sta[++top]=K[i];
            }
            while(top>1)F.tu(sta[top-1],sta[top],dep[sta[top]]-dep[sta[top-1]]),top--;
            dp_up(1);
            dp_dwn(1);
            get_son(1);
            printf("%d
    ",ans);
            //clear_is,gr
            rep(i,1,cnt)is[K[i]]=0;
            rep(i,1,r)in[que[i]]=0;
            r=0;
        }
        return 0; 
    }
    
  • 相关阅读:
    Apollo服务搭建
    常用MIME类型
    eclipse 搭建 swagger-ui(maven项目 springboot框架)
    子页面iframe跨域执行父页面定义的JS方法
    SpringBoot 实现前后端分离的跨域访问(CORS)
    使用 QueryRunner 实现 JDBC 常用操作封装
    纯Java版本的JDBC基础操作,支持查询结果到泛型实体类的转换
    javascript_鼠标划词,弹出选取的词
    js获取url参数值
    纯CSS打造的下拉菜单
  • 原文地址:https://www.cnblogs.com/Sinuok/p/11039319.html
Copyright © 2020-2023  润新知