hdu 4008 树形DP + LCA

hdu 4008 树形DP + LCA
题目描述:
    给一颗结点数为(100,000)的树,最多询问100,000次。每次询问对两个结点X,Y,以X为根,Y的最小标号的孩子,Y的最小标号的后代。
吐槽:
    1. 这么难写的题为什么大家都当水题做阿...
算法分析:
    
    对于X,Y,我们求出其LCA,U。
    如果U是Y的父亲,那么一切都不变。用预处理的结果就可以。
    如果U是Y,那么我们可以快速求出,含有后代X的Y的儿子X'。
    检查X'是否是Y的最小孩子(儿子)。如果是,那么结果是Y的次小儿子(孩子)。否则是Y的最小儿子(孩子)。
    于是我们树形DP,求出最小与次小(儿子,后代)就可以了。写起来很猥琐,容易出错。
    LCA的写法是参照shi哥的代码。
    先预处理求出u的(1,2,4,8,...)代祖先,
    先让x,y处于同一层。
    然后让x,y一点一点向上爬,直到爬到恰好x==y的位置就是LCA了。
    
#include < iostream >
#include<cassert>
#include<cstdio>
using  namespace std;
const  int maxb = 17;
const  int V = 1<<maxb;
int e,P[V][maxb], head[V], nxt[V<<1], pnt[V<<1], mn[V], sd[V], mn1[V],sd1[V];
//  dp
const  int inf = ~0u>>2;
int dep[V];
void dfs( int u, int f){
    P[u][0] = f;
     for( int i =1; i< maxb; i++)
        P[u][i] = P[P[u][i-1]][i-1];
     if(f!=u) mn[u] = f;  else mn[u] = inf;
    mn1[u] = sd1[u] = inf;
    sd[u] = inf;
     for( int i=head[u];i!=-1;i=nxt[i]) {
         int v = pnt[i];
         if(v == f)  continue;
        dep[v] = dep[u] + 1;
         if(v < mn[u]) {sd[u] = mn[u]; mn[u] = v;}
         else  if(v < sd[u]) sd[u] = v;
        dfs(v,u);
         int fst,snd;
         if(v > mn1[v]) {
            fst = mn1[v];
            snd = min(v, sd1[v]);
        }
         else {
            fst = v;
            snd = mn1[v];
        }
         if(fst < mn1[u]){
            sd1[u] = mn1[u];
            mn1[u] = fst;
        }
         else {
            sd1[u] = min(sd1[u],fst);
        }
    }
}
//  build
void add( int u, int v){
    nxt[e] = head[u];
    head[u] = e;
    pnt[e++] = v;
}
//  LCA
void go( int &u, int d){
     for( int i=0;i<maxb;i++){
         if((1<<i) & d) u = P[u][i];
    }
}
int LCA( int x, int y){
     if(dep[x] > dep[y]) swap(x,y);
    go(y,dep[y] - dep[x]);
     if(x == y)  return x;
     for( int i=maxb-1;i>=0;i--){
         if(P[x][i] != P[y][i])
            x = P[x][i], y = P[y][i];
    }
    assert(P[x][0]==P[y][0]);
     return P[x][0];
}
//  work
void work( int x, int y){
     int u = LCA(x,y);
     if(sd[y] == inf) {puts("no answers!");  return;}
     if(u!=y){
        printf("%d %d\n",P[y][0] == mn[y] ? sd[y] : mn[y], mn1[y]);
    }
     else {
        go(x,dep[x]-dep[y]-1);
        
        printf("%d %d\n",x == mn[y] ? sd[y]: mn[y] , y == 1 ? (min(mn1[x],x) == mn1[y]? sd1[y]: mn1[y]): 1);
    }
}
int main(){
     int test;
    scanf("%d",&test);
     while(test--){
         int u,v,n,m;
        e = 0;
        scanf("%d%d",&n,&m);
         for( int i=1;i<=n;i++) head[i] = -1;
         for( int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dep[1] = 0;
        dfs(1,1);
         while(m--){
            scanf("%d%d",&u,&v);
            work(u,v);
        }
        puts("");
    }
}

你可能感兴趣的:(hdu 4008 树形DP + LCA)