codeforces 587C(树上的倍增算法)

分析:

同LCA相似的思路,维护st[ i ][ j ] 代表从节点i往上(不包含i)走2^j个节点前十个最小值集合,这样一直倍增着往上走秩序logn次集合合并即可。

#include <bits/stdc++.h>

#define fst first
#define snd second
#define ALL(a) a.begin(), a.end()
#define clr(a, x) memset(a, x, sizeof a)
#define rep(i,x) for(int i=0;i<(int)x;i++)
#define rep1(i,x,y) for(int i=x;i<=(int)y;i++)
#define LOGN  18
typedef long long ll;
using namespace std;

const int N = 1e5 + 100;
vector<int> G[N] , pre[N];
int posi[N],depth[N],fa[N][LOGN],n;
vector<int> st[N][LOGN];
void get(vector<int>& a , vector<int>& b){
   for(auto v : a) b.push_back(v);
   sort(ALL(b)); while(b.size() > 10) b.pop_back();
}
void dfs(int u,int f,int dep){
   depth[u] = dep; fa[u][0] = f;
   if(f != -1) get(pre[f], st[u][0]);
   for(auto v:G[u])
      if(v != f) dfs( v , u , dep+1);
}
void build(){
   for(int i=0;i<LOGN-1;i++)
      for(int j=1;j<=n;j++){
        fa[j][i+1] = (fa[j][i]==-1 ? -1 : fa[fa[j][i]][i]);
        if(fa[j][i+1] != -1) {
             get(st[j][i] , st[j][i+1]);
             get(st[fa[j][i]][i], st[j][i+1]);
        }
   }
}
void show(int a,vector<int>& ans){
   int lim = min(a , (int)ans.size());
   printf("%d",lim);
   rep(i,lim) printf(" %d",ans[i]);
   printf("\n");
}
int cal(int u,int v ,int a){
    if(depth[u] > depth[v]) swap(u , v);
    int cha = depth[v] - depth[u];
    vector<int> ans;
    get(pre[v] , ans);
    for(int i=LOGN - 1; i>=0;i--){
        if((cha>>i)&1){
            get(st[v][i] , ans);
            v = fa[v][i];
        }
    }
    if(u == v){
        show(a , ans);
        return u;
    }
    get(pre[u] , ans);
    for(int i=LOGN - 1; i>=0;i--){
        if(fa[v][i] != fa[u][i]){
             get(st[v][i] ,ans);
             get(st[u][i] ,ans);
             v = fa[v][i];
             u = fa[u][i];
        }
    }
    get(st[u][0] , ans);
    show(a , ans);
    return fa[u][0];
}
int m,q;
void read(){
   scanf("%d %d %d",&n,&m,&q);
   rep(i,n-1){
       int u, v;
       scanf("%d %d",&u,&v);
       G[u].push_back(v);
       G[v].push_back(u);
   }
   rep1(i,1,m) scanf("%d",&posi[i]),pre[posi[i]].push_back(i);
   dfs(1 , -1 , 0);
   build();
   rep1(i,1,n)if(pre[i].size()){
       sort(ALL(pre[i]));
       while(pre[i].size() > 10) pre[i].pop_back();
   }
   int u , v , a;
   while(q--){
      scanf("%d %d %d",&u,&v,&a);
      cal(u , v , a);
   }
}
int main()
{
   read();
   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值