题意:
一棵n个结点的树上有若干个点是红色。有q个询问,每个询问给出k个点,可以将树上一个非红色点染成红色,使得k个点中与红色点的最大距离最小,输出最小值。根节点是红色。
2 <= n,m <= 1e5, 1 <= q <= 2e5
分析:
搜索的过程中将每个点距离最近红色点的距离和根节点的距离记录下来。
二分答案,然后将k个点中距离红色大于答案的拿出来,求这些点的LCA,判断将这个点染成红色能否使得距离红色点的距离都比答案小。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5+10;
typedef long long ll;
typedef pair<int,int> P;
int n,m,q,r[MAXN],times,rf[MAXN],dep[MAXN],dp[MAXN][20],p[MAXN],mm[MAXN],v[MAXN],tv[MAXN];
ll d[MAXN],rd[MAXN];
vector<P> G[MAXN];
void dfs(int u,int pre){
if(r[u] == 1){
rd[u] = 0; rf[u] = u;
}
dp[++times][0] = u;
p[u] = times;
for(int i=0;i<(int)G[u].size();i++){
int v = G[u][i].first, w = G[u][i].second;
if(v == pre) continue;
dep[v] = dep[u] + 1;
d[v] = d[u] + w;
rd[v] = rd[u] + w;
rf[v] = rf[u];
dfs(v,u);
dp[++times][0] = u;
}
}
void rmq_init(){
for(int j=1;j<=mm[times];j++){
for(int i=1;i+(1<<j)-1<=times;i++){
dp[i][j] = dep[dp[i][j-1]] > dep[dp[i+(1<<(j-1))][j-1]] ? dp[i+(1<<(j-1))][j-1] : dp[i][j-1];
}
}
}
int rmq_query(int a,int b){
if(a > b) swap(a,b);
int k = mm[b-a+1];
return dep[dp[a][k]] < dep[dp[b-(1<<k)+1][k]] ? dp[a][k] : dp[b-(1<<k)+1][k];
}
int lca(int a,int b){
return rmq_query(p[a],p[b]);
}
int ok(ll mid, int k){
int top=0;
for(int i=0;i<k;i++){
if(rd[v[i]] > mid) tv[top++] = v[i];
}
if(top <= 1) return 1; //rather than == !!!
int f = tv[0];
for(int i=1;i<top;i++) f = lca(f,tv[i]);
for(int i=0;i<top;i++) if(dep[f] <= dep[rf[tv[i]]]) return 0;
for(int i=0;i<top;i++) if(mid < rd[tv[i]] - rd[f]) return 0;
return 1;
}
int read(){
char c=getchar();
while (c>'9'||c<'0') c=getchar();
int x=0;
while ('0'<=c && c<='9'){
x=x*10+c-'0'; c=getchar();
}
return x;
}
int main(){
//ios::sync_with_stdio(false);
//freopen("1.txt","r",stdin);
mm[0] = -1;
for(int i=1;i<MAXN;i++){
mm[i] = ((i&(i-1)) == 0) ? mm[i-1]+1 : mm[i-1];
}
int T = read();
while(T--){
n = read(); m = read(); q = read();
times = 0;
for(int i=0;i<=n;i++) G[i].clear(), r[i] = 0;
for(int i=0;i<m;i++){
int x = read(); r[x] = 1;
}
for(int i=0;i<n-1;i++){
int a,b,c;
a = read(); b = read(); c = read();
G[a].push_back(make_pair(b,c));
G[b].push_back(make_pair(a,c));
}
d[1] = 0;
dep[1] = 1;
dfs(1,-1);
rmq_init();
while(q--){
int k;
ll mx = 0;
k = read();
for(int i=0;i<k;i++){
v[i] = read();
mx = max(mx,rd[v[i]]);
}
ll L = 0, R = mx, ans=0;
while(L <= R){
ll mid = L+(R-L)/2;
if(ok(mid,k)){
R = mid - 1;
ans = mid;
}else{
L = mid + 1;
}
}
cout << ans <<'\n';
}
}
return 0;
}