zoukankan      html  css  js  c++  java
  • 【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; 
    }
    
  • 相关阅读:
    CodeForces gym Nasta Rabbara lct
    bzoj 4025 二分图 lct
    CodeForces 785E Anton and Permutation
    bzoj 3669 魔法森林
    模板汇总——快读 fread
    bzoj2049 Cave 洞穴勘测 lct
    bzoj 2002 弹飞绵羊 lct裸题
    HDU 6394 Tree 分块 || lct
    HDU 6364 Ringland
    nyoj221_Tree_subsequent_traversal
  • 原文地址:https://www.cnblogs.com/Sinuok/p/11039319.html
Copyright © 2011-2022 走看看