题目大意:
给出一颗由 n n n个结点的树,根节点为 1 1 1,给出 n − 1 n-1 n−1条无向边 ( x i , y i , w i ) (x_i,y_i,w_i) (xi,yi,wi)表示结点 x i x_i xi与结点 y i y_i yi之间有一条可以互相到达的边,断掉这条边需要花费 w i w_i wi的代价,有 m m m个询问,每个询问给出 k k k个结点,问如何断边能使代价最小的时候结点 1 1 1不与这 k k k个结点连通。
x
i
,
y
i
≤
n
,
w
i
≤
1
e
5
x_i,y_i≤n,w_i≤1e5
xi,yi≤n,wi≤1e5
保
证
k
个
节
点
中
不
含
1
保证k个节点中不含1
保证k个节点中不含1
Σ
k
≤
500000
Σk≤500000
Σk≤500000
分析:
树形dp:
设
f
[
i
]
f[i]
f[i]表示断掉了以
i
i
i为根的子树中需要隔绝的结点所需要的最小代价(不包括
i
i
i),
m
l
e
n
[
i
]
mlen[i]
mlen[i]表示从根结点
1
1
1到结点
i
i
i中经过的花费最小的边。
转移显然,
f
[
i
]
=
m
i
n
(
∑
f
s
o
n
i
,
m
l
e
n
[
i
]
)
f[i]=min(\sum f_{son_i},mlen[i])
f[i]=min(∑fsoni,mlen[i])
然后对于这个东西,直接做时间就达到了
O
(
m
n
)
O(mn)
O(mn),肯定
T
L
E
TLE
TLE
我们就观察到
Σ
k
≤
500000
Σk≤500000
Σk≤500000,
那么我们就可以快乐地对这个东西建一颗虚树,然后再跑树形dp
建虚树求lca的时候可以快乐树剖然后求(毒瘤)
对于虚树的概念,可以理解成利用
d
f
s
dfs
dfs序,留下关键点,以及利用
l
c
a
lca
lca将他们连接起来。
时间复杂度就可以变成跟虚树的点数相关,
模拟一下可以发现点数不会超过
2
∗
k
2*k
2∗k,
那么时间复杂度就是
O
(
2
∗
Σ
k
)
O(2*Σk)
O(2∗Σk)
代码:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#define N 250005
using namespace std;
typedef long long ll;
struct Node { int To, w, nxt; }e[N*2];
int Stack[N], Size[N], begind[N], deep[N], hson[N], dfn[N], fa[N], ls[N], a[N];
int n, m, Q, top, cnt, num;
ll mlen[N];
vector <int> dop[N];
int read(int &x)
{
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s >= '0' && s <= '9') { x = x * 10 + (s - '0'); s = getchar(); }
x = x * f;
}
void write(ll x)
{
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
bool cmp(int aa, int bb)
{
return dfn[aa] < dfn[bb];
}
void Addedge(int u, int v, int w)
{
e[++cnt].To = v, e[cnt].w = w, e[cnt].nxt = ls[u], ls[u] = cnt;
e[++cnt].To = u, e[cnt].w = w, e[cnt].nxt = ls[v], ls[v] = cnt;
}
void Jiabian(int u, int v)
{
dop[u].push_back(v);
}
void dfs1(int x, int father)
{
Size[x] = 1, fa[x] = father;
for (int i = ls[x]; i; i = e[i].nxt)
{
if (e[i].To == father) continue;
int y = e[i].To;
mlen[y] = min(mlen[x], (ll)e[i].w);
deep[y] = deep[x] + 1;
dfs1(y, x); Size[x] += Size[y];
if (Size[y] > Size[hson[x]]) hson[x] = y;
}
}
void dfs2(int x, int firstd)
{
begind[x] = firstd; dfn[x] = ++num;
if (hson[x]) dfs2(hson[x], firstd);
for (int i = ls[x]; i; i = e[i].nxt)
if (!begind[e[i].To]) dfs2(e[i].To, e[i].To);
}
int Get_lca(int x, int y)
{
while (begind[x] != begind[y])
{
if (deep[begind[x]] < deep[begind[y]]) swap(x, y);
x = fa[begind[x]];
}
return ((deep[x] > deep[y]) ? y : x);
}
void Insert(int x)
{
if (top == 1) { Stack[++top] = x; return; }
int LCA = Get_lca(x, Stack[top]);
if (LCA == Stack[top]) return;
while(top > 1 && dfn[Stack[top - 1]] >= dfn[LCA])
Jiabian(Stack[top - 1], Stack[top]), top--;
if(LCA != Stack[top]) Jiabian(LCA, Stack[top]), Stack[top] = LCA;
Stack[++top] = x;
}
ll Work_dp(int x)
{
if (!dop[x].size()) return mlen[x];
ll sum = 0;
for (int i = 0; i < dop[x].size(); i++) sum += Work_dp(dop[x][i]);
dop[x].clear();
return min(sum, mlen[x]);
}
int main()
{
read(n);
for (int i = 1; i < n; i++)
{
int u, v, w;
read(u); read(v); read(w);
Addedge(u, v, w);
}
deep[1] = 1, mlen[1] = 1ll<<60, dfs1(1, -1);
dfs2(1, 1);
read(Q);
while (Q--)
{
read(m);
for (int i = 1; i <= m; i++) scanf("%d", &a[i]);
sort(a + 1, a + m + 1, cmp);
top = 1; Stack[1] = 1;
for (int i = 1; i <= m; i++) Insert(a[i]);
while (top > 1) Jiabian(Stack[top - 1], Stack[top]), top--;
write(Work_dp(1)); printf("\n");
}
return 0;
}