hdu 6031 Innumerable Ancestors (虚树 lca)

hdu6031

一棵树(根节点是 1),给出两个集合,集合是 由树上的节点组成的。从两个集合中分别选一个元素,求出他们的 lca,问:lca 的深度最大是多少。

每个询问,两个集合,建虚树,然后dfs一遍,记录子树有没有A点、有没有B点,然后看既有A点又有B点的就更新深度到答案。

#include <bits/stdc++.h>
using namespace std;

const int maxN=100000+10;
const int maxM=100000+10;
const int max_log_n=21;

int n,m,Head[maxN],eg[maxM<<1],nxt[maxM<<1],tot=0;
void init() {
  memset(Head,0,sizeof(Head)); memset(eg,0,sizeof(eg)); memset(nxt,0,sizeof(nxt)); tot=0;
}
void addEdge(int u,int v) {
  eg[++tot]=v; nxt[tot]=Head[u]; Head[u]=tot;
  eg[++tot]=u; nxt[tot]=Head[v]; Head[v]=tot;
}

int id[maxN],dfsclock=0,pa[max_log_n][maxN],dep[maxN];
void dfs(int v,int fa) {
  id[v]=++dfsclock;
  int k=1;
  pa[0][v]=fa;
  dep[v]=fa==-1?1:dep[fa]+1;
  while(pa[k-1][v]!=-1) {
    pa[k][v]=pa[k-1][pa[k-1][v]];
    k++;
  }
  for(int i=Head[v];i;i=nxt[i]) {
    int u=eg[i];
    if(u!=fa) dfs(u,v);
  }
}

int getLca(int u,int v) {
  if(dep[u]>dep[v]) swap(u,v);
  for(int k=max_log_n-1;k>=0;k--) {
    if(((dep[v]-dep[u])>>k)&1) v=pa[k][v];
  }
  if(u==v) return u;
  for(int k=max_log_n-1;k>=0;k--) {
    if(pa[k][v]!=pa[k][u]) v=pa[k][v],u=pa[k][u];
  }
  return pa[0][v];
}

vector<int > As,Bs,all;
bool A[maxN],B[maxN];
bool cmp_by_id(int A,int B) {
  return id[A]<id[B];
}

vector<int > g[maxN];
bool recEg[maxN];
vector<int > used;
void new_addEdge(int u,int v) {
  g[u].push_back(v); g[v].push_back(u);
  if(!recEg[v]) used.push_back(v),recEg[v]=true;
  if(!recEg[u]) used.push_back(u),recEg[u]=true;
}

int stk[maxN],tp=0;

int ans=0;
bool havA[maxN],havB[maxN],vis[maxN];
void Recor(int v,int fa) {
  if(A[v]) havA[v]=true;
  if(B[v]) havB[v]=true;
  for(int i=0;i<(int)g[v].size();i++) {
    int u=g[v][i];
    if(u!=fa) Recor(u,v),havA[v]|=havA[u],havB[v]|=havB[u];
  }
  if(havA[v] && havB[v]) ans=max(ans,dep[v]);
}

int main() {
  int k,x;
  while(scanf("%d%d",&n,&m)==2) {
    init();
    memset(pa,-1,sizeof(pa)); memset(dep,0,sizeof(dep));
    memset(id,0,sizeof(id)); dfsclock=0;
    int u,v;
    for(int i=1;i<=n-1;i++) {
      scanf("%d%d",&u,&v);
      addEdge(u,v);
    }
    dfs(1,-1);
    for(int z=0;z<m;z++) {
      scanf("%d",&k);
      for(int i=1;i<=k;i++) {
        scanf("%d",&x);
        A[x]=true; As.push_back(x);
        if(!vis[x]) all.push_back(x);
        vis[x]=true;
      }
      scanf("%d",&k);
      for(int i=1;i<=k;i++) {
        scanf("%d",&x);
        B[x]=true; Bs.push_back(x);
        if(!vis[x]) all.push_back(x);
        vis[x]=true;
      }
      sort(all.begin(),all.end(),cmp_by_id);
      tp=0;
      stk[++tp]=1;
      for(int i=0;i<(int)all.size();i++) {
        int lca=getLca(stk[tp],all[i]);
        if(lca==stk[tp]) stk[++tp]=all[i];
        else {
          while(tp>=2 && dep[stk[tp-1]]>=dep[lca]) new_addEdge(stk[tp],stk[tp-1]),tp--;
          if(lca!=stk[tp]) new_addEdge(stk[tp],lca);
          tp--;
          stk[++tp]=lca; stk[++tp]=all[i];
        }
      }
      while(tp-1) new_addEdge(stk[tp],stk[tp-1]),tp--;

      ans=0;
      Recor(1,-1);
      printf("%d\n",ans);

      for(int i=0;i<(int)As.size();i++) A[As[i]]=false;
      for(int i=0;i<(int)Bs.size();i++) B[Bs[i]]=false;
      As.clear(); Bs.clear();
      for(int i=0;i<(int)all.size();i++) vis[all[i]]=false;
      all.clear();

      for(int i=0;i<(int)used.size();i++) {
        recEg[used[i]]=false,g[used[i]].clear();
        havA[used[i]]=false,havB[used[i]]=false;
      }
      used.clear();
    }
  }
  return 0;
}

原文地址:https://www.cnblogs.com/darkroome/p/9198553.html

时间: 2024-11-10 11:54:27

hdu 6031 Innumerable Ancestors (虚树 lca)的相关文章

hdu 6031 Innumerable Ancestors(LCA+剪枝)

题目链接:hdu 6031 Innumerable Ancestors 题意: 给你一棵n个节点的树,现在有m个询问,每次给你两个点集a,b. 让你从a,b点集中选两个点x,y,使得这两个点的LCA的深度最大. 题解: 标解应该是二分+LCA,不过我试了一下暴力,稍微剪了点枝,就直接过去了. 具体看代码 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;i++) 3 using namespace std; 4 c

HDU 6031 Innumerable Ancestors

树状数组,倍增,枚举,$dfs$序. 对于每一次的询问,可以枚举$B$集合中的所有点,对于每一个点,在树上二分$LCA$,找到最低的更新答案. 判断是否是$LCA$可以搞个$dfs$序,将$A$集合中所有点标$1$,然后查询子树对应的区间上的区间和. #include <bits/stdc++.h> using namespace std; const int maxn = 100010; int L[maxn],R[maxn]; int c[2*maxn]; int h[maxn]; int

[SDOI2011][BZOJ2286] 消耗战|虚树|树型dp|树上倍增LCA

2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1040  Solved: 363[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸

【BZOJ2286】【SDOI2011】消耗战 LCA单调性(构建虚树)+树形DP

题解: 首先我们考虑每次都做一遍树形DP(树形DP自己脑补去,随便乱搞就过了). 显然这是TLE无疑的. 所以可以利用LCA单调性构建虚树. 思想: 我们发现每次树形DP有很多点用不到,但是却需要被扫过,让他们见鬼去吧! 实现: 我们只对每次扫的图插入本次询问需要的节点,以及它们的LCA. 这样询问了m个点,虚树就至多只需要2m个点(so quick). 而插入顺序上不妨利用LCA单调性来把点按dfs度排个序,然后挨个插入单调栈. 同时我们要保证单调栈维护的是一条链,也就是一旦不是链了,我们自然

[HNOI2014][BZOJ3572] 世界树|虚树|树上倍增LCA|树型dp|dfs序

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 555  Solved: 319[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.    世界树的形态可以用一个数学模型来描述:世界树中有n个种

bzoj-2286 消耗战【虚树+倍增lca+单调栈】

2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 1815  Solved: 645 [Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是

虚树初探

虚树其实没什么的.. 只是因为点太多了不能全开于是只开那些需要用到的点. 一棵虚树包括要求点以及它们的lca.. 虚树的构建...(其实感觉如果会虚树的构建的话接下来就是树dp啦没什么的... 首先我们应该对整棵树dfs,求出它的dfs序列.然后对于给的点,按dfs排序.. 因为我们是按dfs序排列的,所以虚树一定是由一条条链构成的.. 扫一遍给的点,如果这个点在当前的这条链上,那加在栈顶就可以了. 如果不是的话,那就不断地退栈使的原来的那条链上面的边全部被加到边集中.. rep(i,1,n){

bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)

2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1276  Solved: 445[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸

bzoj 3572 [Hnoi2014]世界树(虚树+DP)

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 645  Solved: 362[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.     世界树的形态可以用一个数学模型来描述:世界树中有n个