codeforces 587C:(LCA倍增+维护最小值)

一开始直接无脑tarjan,回溯只能一层层往上走,太慢了,加了各种优化还是TLE

后来了解到LCA倍增法(在线)。复杂度其实相比LCA转RMQ以及tarjan是要稍差一些,但是其中能同步维护的只有LCA倍增,很神奇的算法

#include"cstdio"
#include"queue"
#include"cmath"
#include"stack"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"queue"
#include"map"
#include"vector"
#define ll long long

using namespace std;
const int MAXN = 1e5+50;
const int MAXE = 200500;
const int INF = 0x3f3f3f;

struct node{
    int e,next;
    node(){}
    node(int a,int b):e(a),next(b){}
}edge[MAXE];

int n,m,q,tot;
int vis[MAXN],first[MAXN],deep[MAXN];
int p[MAXN][21];                    ///记录i往上跳2^j下到达的结点
vector<int> G[MAXN][21],ans,num[MAXN];///G[i][j]记录i往上2^j个结点的10个最小值(不包括i结点,为的是更新方便)

void init(){
    tot=0;
    memset(vis,0,sizeof(vis));
    memset(first,-1,sizeof(first));
    memset(p,-1,sizeof(p));
}

void addedge(int u,int v){
    edge[tot]=node(v,first[u]);
    first[u]=tot++;
    edge[tot]=node(u,first[v]);
    first[v]=tot++;
}

void update(vector<int> a,vector<int> b,vector<int> &c){    ///合并
    int sa=a.size();
    int sb=b.size();
    int i=0,j=0;
    int va,vb;
    while(i+j<10&&(i<sa||j<sb)){
        if(i<sa) va=a[i];
        else va=INF;
        if(j<sb) vb=b[j];
        else vb=INF;
        if(va<vb){
            c.push_back(va);
            i++;
        }
        else{
            c.push_back(vb);
            j++;
        }
    }
}

void Merge(vector<int> &a,vector<int> b){   ///合并
    int sb=b.size();
    for(int i=0;i<sb;i++) a.push_back(b[i]);
}

void init_p(){
    for(int j=1;(1<<j)<=n;j++)
    for(int i=1;i<=n;i++) if(p[i][j-1]!=-1) {
        p[i][j]=p[p[i][j-1]][j-1];                  ///i往上跳2^j相当于i往上跳2次2^(j-1)
        update(G[i][j-1],G[p[i][j-1]][j-1],G[i][j]);
    }
}

void dfs(int u,int dep){
    vis[u]=1;
    deep[u]=dep;
    for(int i=first[u];i!=-1;i=edge[i].next){
        int v=edge[i].e;
        if(vis[v]) continue;
        p[v][0]=u;          ///初始化,2^0=1,相当于父亲节点
        G[v][0]=num[u];     ///初始化
        dfs(v,dep+1);
    }
}

void get_lca(int u,int v,int k){
    if(deep[u]<deep[v]) swap(u,v);  ///保证deep[u]>deep[v]便于操作

    int a=u,b=v;        ///先不合并u,v内的元素,以免重复,故先保存下来
    ans.clear();

    int dif=deep[u]-deep[v];
    for(int i=0;i<=20;i++) if(dif&(1<<i)){
        Merge(ans,G[u][i]);
        u=p[u][i];
    }
    if(u!=v){
        for(int i=20;i>=0;i--) if(p[u][i]!=-1&&p[u][i]!=p[v][i]){
            Merge(ans,G[v][i]);
            Merge(ans,G[u][i]);
            v=p[v][i];
            u=p[u][i];
        }
        Merge(ans,num[a]);          ///这种情况是u,v分属lca的两个子树
        Merge(ans,num[b]);
        Merge(ans,num[p[u][0]]);
    }
    else Merge(ans,num[a]);         ///这种情况下v就是LCA,故只合并u
    sort(ans.begin(),ans.end());
    int t=min(k,(int)ans.size());
    cout<<t<<' ';
    for(int i=0;i<t;i++) cout<<ans[i]<<' ';
    cout<<endl;
}

int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d%d%d",&n,&m,&q);
    init();

    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        addedge(u,v);
    }
    for(int i=1;i<=m;i++){
        int t;scanf("%d",&t);
        if(num[t].size()<10) num[t].push_back(i);
    }
    dfs(1,1);
    init_p();
    for(int i=0;i<q;i++){
        int u,v,k;
        scanf("%d%d%d",&u,&v,&k);
        get_lca(u,v,k);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/luxiaoming/p/5094016.html