4048(2018 青島網路賽B) LCA + 二分思想
阿新 • • 發佈:2018-12-10
題目連結:點選這裡
解題思路:
首先預處理出原來樹中節點u的C(u)值,那麼在給出的ki個節點後,將他們根據原C()大小從大到小排序.
那麼假設最小情況下最大值不會超過第i個C()值,那麼意思就是前i個節點的C()值要做出改變才行,怎麼讓他們都改變呢?
將紅點設在前i個點的最近公共祖先上,就可以將他們都改變,且儘量最小.
對於前i個點C()的變化後最大值也可以遞推求出.
#include<bits/stdc++.h> #define inf 0x3f3f3f3f #define fi first #define se second using namespace std; typedef long long ll; const int mx = 1e5 + 10; int t,n,m,R[mx],cnt[mx],fa[mx][20]; int deep[mx]; ll D[mx],dis[mx]; bool red[mx]; vector <pair<int,int>> vec[mx]; void dfs(int x,int f) { if(red[x]) R[x] = x; else R[x] = R[f]; dis[x] = (D[x]-D[R[x]]); for(auto v:vec[x]) { if(v.fi==f) continue; D[v.fi] = D[x] + v.se; deep[v.fi] = deep[x] + 1; fa[v.fi][0] = x; dfs(v.fi,x); } } bool cmp(ll a,ll b) { return dis[a] > dis[b]; } void RMQ(int N){ for(int j=1;(1<<j)<=N;j++) for(int i=1;i<=N;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; } int LCA(int u,int v){ if(deep[u]<deep[v]) swap(u,v); int de=log(deep[u])/log(2.0); for(int i=de;i>=0;i--) if(deep[u]-(1<<i)>=deep[v]) u=fa[u][i]; if(v==u) return u; for(int i=de;i>=0;i--){ if(fa[u][i]!=fa[v][i]){ u=fa[u][i]; v=fa[v][i]; } } return fa[v][0]; } int main() { int t,q; scanf("%d",&t); while(t--){ scanf("%d%d%d",&n,&m,&q); int a,b,c; for(int i=1;i<=n;i++) red[i] = 0,vec[i].clear(); for(int i=1;i<=m;i++){ scanf("%d",&a); red[a] = 1; } dis[n+1] = D[1] = dis[1] = 0; deep[1] = 1; for(int i=1;i<n;i++){ scanf("%d%d%d",&a,&b,&c); vec[a].push_back(make_pair(b,c)); vec[b].push_back(make_pair(a,c)); } dfs(1,0); RMQ(n); int k; while(q--){ scanf("%d",&k); ll ans = 0,maxx = 0,lon; for(int i=0;i<k;i++){ scanf("%d",cnt+i); } cnt[k] = n+1; sort(cnt,cnt+k,cmp); ans = dis[cnt[0]]; int far = cnt[0],mom,Mdep = deep[R[cnt[0]]]; ans = min(ans,dis[cnt[1]]); for(int i=1;i<k;i++) { mom = LCA(far,cnt[i]); Mdep = max(Mdep,deep[R[cnt[i]]]); if(deep[mom]<deep[far]) maxx += D[far] - D[mom]; if(Mdep>=deep[mom]) break; maxx = max(D[cnt[i]]-D[mom],maxx); far = mom; ans = min(ans,max(maxx,dis[cnt[i+1]])); } printf("%lld\n",ans); } } return 0; }