一开始直接无脑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; }
时间: 2024-10-23 15:26:01