Duff in the Army 树上倍增+LCA CodeForces - 587C

题目链接:CodeForce _587C

主要思路:

像倍增处理FA数组一样,倍增处理每个节点到其任一一个祖先的所经过的点集。如Po[i][1]表示从i这个点向上走(2^1)-1步所走过的点集。(由于Po[i][0]表示i这个点的点集,故实际意义中2^i要-1)。最后在加上没有加上的点即可。(听不懂可以看代码)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define M 100005
using namespace std;
struct E {
    int nx,to;
} edge[M<<1];
int tot,head[M];
void Addedge(int a,int b) {//正向表存边 
    edge[++tot].to=b;
    edge[tot].nx=head[a];
    head[a]=tot;
}
struct path {
    int sz;
    int num[12];//由于题目只让求前10,故只用开那么大,不然会MLE。 
    Init(){
        sz=0;
    }
    path() {
        sz=0;
    }
    void operator+=(const int &x){//从定义运算符看着比较舒服 
        int p=lower_bound(num,num+sz,x)-num;
        for(int i=sz;i>=p;i--)num[i]=num[i-1];//插入排序 
        num[p]=x;
        sz=min(sz+1,10);
    }
    path operator+(const path&_)const {
        path res;
        int i=0,j=0,k=0;
        while(k<10&&i<sz&&j<_.sz) {//归并,注意k不能大于10,每个语句都要加上。 
            if(num[i]<=_.num[j])res.num[k++]=num[i++];
            else res.num[k++]=_.num[j++];
        }
        while(i<sz&&k<10)res.num[k++]=num[i++];
        while(j<_.sz&&k<10)res.num[k++]=_.num[j++];
        res.sz=k;
        return res;
    }
} p[M][20];
int fa[M][20],dep[M],dis[M];
void dfs(int now) {
    for(int i=1;(1<<i)<=dep[now];i++){//不能跳过根节点。这条语句也可以改一下放在主函数中dfs完毕后执行 
        fa[now][i]=fa[fa[now][i-1]][i-1];
        p[now][i]=p[now][i-1]+p[fa[now][i-1]][i-1];
    }
    for(int i=head[now]; i; i=edge[i].nx) {
        int nxt=edge[i].to;
        if(nxt==fa[now][0])continue;
        fa[nxt][0]=now;
        dep[nxt]=dep[now]+1;
        dfs(nxt);
    }
}
path res;
void Up(int &x,int y){
    for(int i=0;i<20;i++){
        if((1<<i)&y){
            res=res+p[x][i];
            x=fa[x][i];
        }
    }
}
void LCA(int a,int b) {
    if(dep[a]>dep[b])swap(a,b);
    Up(b,dep[b]-dep[a]);
    if(a==b){
        res=res+p[a][0];
        return;
    }
    for(int i=19;i>=0;i--){
        if(fa[a][i]!=fa[b][i]){
            res=res+p[a][i]+p[b][i];//res跟着更新 
            a=fa[a][i];
            b=fa[b][i];
        }
    }
    res=res+p[a][1]+p[b][0];//加上剩余没加的三个结点的点集 
    return ;//相信学过LCA的同学对此都不陌生 
}
int val[M];
int main() {
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1; i<n; i++) {
        int a,b;
        scanf("%d%d",&a,&b);
        Addedge(a,b);
        Addedge(b,a);
    }
    for(int i=1; i<=m; i++) {
        scanf("%d",&val[i]);
        p[val[i]][0]+=i;//初始状态 
    }
    dep[1]=1; 
    dfs(1);
    for(int i=1; i<=q; i++) {
        res.Init();
        int u,v,a;
        scanf("%d%d%d",&u,&v,&a);
        LCA(u,v);
        int k=min(a,res.sz);
        printf("%d ",k);
        for(int i=0;i<k;i++)printf("%d ",res.num[i]);
        puts("");
    }
}

猜你喜欢

转载自blog.csdn.net/qq_35320178/article/details/81394753