题意:给出一棵树,其中某些点是红色,其余点是黑色。定义一个点的花费为这个点到距其最近的红色祖先节点的距离。要求进行q次查询,每次查询给出k个节点,允许将最多一个黑色点变为红色, 求这k个点中最大花费的最小值。每次查询相互独立,不对树的结构产生影响。
首先可以预处理出每个点的最近红色祖先以及花费。
在查询的k个点中,需要修改的只可能是从花费最大的点到它的最近红色祖先的路径上的点,否则最大值不会得到优化。对这k个点花费从大到小进行排序,初始的时候将修改的点now定在最大和次大的LCA ,此时尝试去把这个LCA变为红色,在最大点到当前LCA的花费与这个LCA对应子树之外第一个位置的花费中取最大值,这就是此时的花费最大值。然后依次扫过去整体取最小值。。。单次查询可以在klogk的时间内完成。
while(1) 表达能力 - -;实锤了
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 100050;
const ll INF = (1LL << 62) - 1;
const double eps = 1e-8;
int t, n, m, q, k, x, no, a, b, pre[maxn][35];
int head[maxn], dep[maxn], nred[maxn], p[maxn];
ll dis[maxn], w, ans, res;
bool vis[maxn], flag[maxn];
struct node
{
int to, nxt;
ll w;
}e[maxn << 1];
void add(int a, int b, ll w)
{
e[no].to = b, e[no].nxt = head[a], e[no].w = w;
head[a] = no++;
e[no].to = a, e[no].nxt = head[b], e[no].w = w;
head[b] = no++;
}
bool cmp(int a, int b)
{
return dis[a] - dis[nred[a]] > dis[b] - dis[nred[b]];
}
void init_lca()
{
for(int j = 1;(1<<j) <= n;j++)
{
for(int i = 1;i <= n;i++)
pre[i][j] = -1;
}
for(int j = 1;(1<<j) <= n;j++)
{
for(int i = 1;i <= n;i++)
{
if(pre[i][j - 1] != -1)
pre[i][j] = pre[pre[i][j - 1]][j - 1];
}
}
}
int lca(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
int mlg = 0;
while((1<<mlg) <= dep[x]) mlg++;
mlg--;
for(int i = mlg;i >= 0;i--)
{
if(dep[x] - (1<<i) >= dep[y])
x = pre[x][i];
}
if(x == y) return x;
for(int i = mlg;i >= 0;i--)
{
if(pre[x][i] != -1 && pre[x][i] != pre[y][i])
x = pre[x][i], y = pre[y][i];
}
return pre[x][0];
}
void init()
{
no = dis[1] = 0;
memset(vis, 0, sizeof(vis));
memset(head, -1, sizeof(head));
memset(flag, 0, sizeof(flag));
}
void dfs(int u, int red, int fa, int num)
{
flag[u] = 1;
nred[u] = red;
dep[u] = num;
pre[u][0] = fa;
for(int i = head[u];i != -1;i = e[i].nxt)
{
int v = e[i].to;
if(flag[v]) continue;
dis[v] = dis[u] + e[i].w;
if(vis[v]) dfs(v, v, u, num + 1);
else dfs(v, red, u, num + 1);
}
}
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d%d%d", &n, &m, &q);
init();
for(int i = 1;i <= m;i++)
{
scanf("%d", &x);
vis[x] = 1;
}
for(int i = 1;i < n;i++)
{
scanf("%d%d%lld", &a, &b, &w);
add(a, b, w);
}
dfs(1, 1, 0, 1);
init_lca();
while(q--)
{
scanf("%d", &k);
for(int i = 0;i < k;i++)
scanf("%d", &p[i]);
sort(p, p + k, cmp);
int now = p[0], i = 0;
ans = INF;
while(i < k)
{
int Lca = lca(now, p[i]);
while(i < k && Lca == lca(now, p[i]) && nred[now] == nred[p[i]]) i++;
res = dis[p[0]] - dis[nred[p[0]]] - dis[Lca] + dis[nred[Lca]];
if(i < k) res = max(res, dis[p[i]] - dis[nred[p[i]]]);
if(res < ans) ans = res;
else break;
now = Lca;
}
printf("%lld\n", ans);
}
}
return 0;
}