参考:传送门
树上的路径可以分为两类,一类是经过当前子树的根结点的,另一类是在当前子树内的。
这两种路径里找加起来长度为k的就行了。
第一类路径可以直接算(当前子树的根为lca), 第二类路径每次递归到重心处理下去,就可以转化为第一种路径
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 7, maxm = 1e7, inf = 1e9+7;
struct edge {
int v, w, nxt;
}e[maxn << 1];
int n, m, siz[maxn], mx[maxn], vis[maxn], f[maxn], g[10000007], tot;
int head[maxn], eid, root, ans[maxn], sum, K[1007], h[1007];
int dis[maxn], stk[maxn], top;
void init() {
memset(head, -1, sizeof(head));
eid = 0;
}
void insert(int u, int v, int w) {
e[eid].v = v;
e[eid].w = w;
e[eid].nxt = head[u];
head[u] = eid++;
}
void getroot(int u, int fa) {
siz[u] = 1; mx[u] = 0;
for (int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if (vis[v] || v == fa) continue;
getroot(v, u);
siz[u] += siz[v];
mx[u] = max(mx[u], siz[v]);
}
mx[u] = max(mx[u], sum-mx[u]);
if (mx[u] < mx[root]) root = u;
}
void getdis(int u, int fa) { //第一类
f[++tot] = dis[u];
for (int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa || vis[v]) continue;
dis[v] = dis[u] + e[i].w;
getdis(v, u);
}
}
void calc(int u) {
top = 0;//g是一个桶 存出现过的第二类路径的长度
for (int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if(vis[v]) continue;
tot = 0;
dis[v] = e[i].w;
getdis(v, u);
for (int j = tot; j; j--) //这里会被卡成O(nm) 还是能过
for (int k = 1; k <= m; k++)
if (K[k] >= f[j]) {
h[k] |= g[K[k]-f[j]];
}
for (int j = tot; j; j--) {
if(f[j] <= maxm)
stk[++top] = f[j], g[f[j]] = 1;
}
}
for (int i = 1; i <= top; i++)
g[stk[i]] = 0;
}
void sol(int u) {
vis[u] = g[0] = 1;
calc(u);
for (int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if (!vis[v]) {
sum = siz[v]; root = 0;
mx[0] = inf;
getroot(v, 0);
sol(root);
}
}
}
int rd() {
int s = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
return s * f;
}
int main() {
n = rd(), m = rd(); init();
for (int i = 1; i < n; i++) {
int u = rd(), v = rd(), w = rd();
insert(u, v, w); insert(v, u, w);
}
for (int i = 1; i <= m; i++) K[i] = rd();
mx[0] = sum = n;
getroot(1, 0);
sol(root); //先找整颗树重心进行分治
for (int i = 1; i <= m; i++) {
if (h[i]) puts("AYE");
else puts("NAY");
}
}