这道题的思路感觉挺赞的……
暴力的DP,f[i]表示切到i点的最小代价,显然有f[u]=min{vip[v]?inf:f[v],dis(u,v)}。
这其中中间有很多dp都是不必要的,因为如果一条链D下来,那么两个关键点之间更新的权值都是相邻两点的距离,可以省略掉。
然后就可以用一个应该是挺经典的做法:用单调栈维护一条链。
这样思路就很明显了,先按dfs序排序,然后用单调栈维护一条当前DP的链,每当加入一个新的关键点的时候就看看它是不是接在了链上,不然就一直往上跑也就是弹栈,直到接到了链上,在弹的过程中DP就可以了。
感觉这个做法可以用在很多跟删除连接关键点的边有关的题目里面。
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i = a , _ = b ; i <= _ ; i ++)
#define per(i,a,b) for(int i = a , _ = b ; i >= _ ; i --)
#define fore(i,u) for(int i = head[u] ; i ; i = nxt[i] )
#define maxn 250007
#define maxm 500007
inline int rd() {
char c = getchar();
while (!isdigit(c)) c = getchar() ; int x = c - '0';
while (isdigit(c = getchar())) x = x * 10 + c - '0';
return x;
}
template<class T> inline void upmin(T&a , T b) { if (a > b) a = b ; }
typedef long long ll;
const ll inf = 1ll << 60 ;
typedef int arr_int[maxn];
typedef ll arr_ll [maxn];
typedef int adj[maxm];
arr_int head , pos , dep , h , st , g;
arr_ll f;
adj to , nxt , val ;
int dis[maxn][19];
int fa[maxn][19];
int n , ett , dfs_clock , top;
inline void ins(int u , int v , int w) {
to[++ ett] = v , val[ett] = w , nxt[ett] = head[u] , head[u] = ett;
}
void dfs(int u) {
pos[u] = ++ dfs_clock;
rep (i , 1 , 18) fa[u][i] = fa[fa[u][i - 1]][i - 1] , dis[u][i] = min(dis[fa[u][i - 1]][i - 1] , dis[u][i - 1]);
fore (i , u) {
int v = to[i] , w = val[i];
if (v == fa[u][0]) continue;
dep[v] = dep[u] + 1 , fa[v][0] = u , dis[v][0] = w;
dfs(v);
}
}
inline int lca(int u , int v) {
if (dep[u] < dep[v]) swap(u , v);
int d = dep[u] - dep[v];
rep (i , 0 , 18) if ((1 << i) & d) u = fa[u][i];
if (u == v) return u;
per (i , 18 , 0) if (fa[u][i] != fa[v][i]) u = fa[u][i] , v = fa[v][i];
return fa[u][0];
}
inline ll Dis(int u , int v) {
if (dep[u] < dep[v]) swap(u , v);
int d = dep[u] - dep[v];
int ret = 0x7fffffff;
rep (i , 0 , 18) if ((1 << i) & d) upmin(ret , dis[u][i]) , u = fa[u][i];
if (u == v) return ret;
per (i , 18 , 0) if (fa[u][i] != fa[v][i]) upmin(ret , min(dis[u][i] , dis[v][i])) , u = fa[u][i] , v = fa[v][i];
upmin(ret , min(dis[u][0] , dis[v][0]));
return (ll) ret;
}
void input() {
n = rd();
rep (i , 2 , n) {
int u = rd() , v = rd() , w = rd();
ins(u , v , w) , ins(v , u , w);
}
dep[1] = 1 , dfs(1);
}
void work(int k) {
st[top = 1] = 1;
f[top] = g[top] = 0;
rep (i , 1 , k) {
int p = lca(st[top] , h[i]);
while (dep[st[top]] > dep[p]) {
if (dep[st[top - 1]] <= dep[p]) {
ll tmp = min(g[top] ? inf : f[top] , Dis(p , st[top]));
st[top --] = 0;
if (st[top] != p) {
st[++ top] = p;
f[top] = 0 , g[top] = 0;
}
f[top] += tmp;
break;
} else {
f[top - 1] += min(g[top] ? inf : f[top] , Dis(st[top - 1] , st[top]));
st[top --] = 0;
}
}
if (st[top] != h[i]) {
st[++ top] = h[i];
f[top] = 0;
}
g[top] = 1;
}
for (;top > 1; st[top --] = 0)
f[top - 1] += min(g[top] ? inf : f[top] , Dis(st[top - 1] , st[top]));
printf("%lld\n" , f[1]);
}
bool cmp(const int a , const int b) {
return pos[a] < pos[b];
}
void solve() {
rep (m , 1 , rd()) {
int k = rd();
rep (i , 1 , k) h[i] = rd();
sort(h + 1 , h + k + 1 , cmp);
work(k);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("data.txt" , "r" , stdin);
#endif
input();
solve();
return 0;
}