题目描述
在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。
输入输出格式
输入格式:
第一行一个整数n,代表岛屿数量。
接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n且1<=c<=100000。
第n+1行,一个整数m,代表敌方机器能使用的次数。
接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。
输出格式:
输出有m行,分别代表每次任务的最小代价。
输入输出样例
输入样例#1:
10
1 5 13
1 9 6
2 1 19
2 4 8
2 3 91
5 6 8
7 5 4
7 8 31
10 7 9
3
2 10 6
4 5 7 8 3
3 9 4 6
输出样例#1:
12
32
22
说明
【数据规模和约定】
对于
10%
10
%
的数据,
2<=n<=10,1<=m<=5,1<=ki<=n−1
2
<=
n
<=
10
,
1
<=
m
<=
5
,
1
<=
k
i
<=
n
−
1
对于
20%
20
%
的数据,
2<=n<=100,1<=m<=100,1<=ki<=min(10,n−1)
2
<=
n
<=
100
,
1
<=
m
<=
100
,
1
<=
k
i
<=
m
i
n
(
10
,
n
−
1
)
对于
40%
40
%
的数据,
2<=n<=1000,m>=1,∑ki<=500000,1<=ki<=min(15,n−1)
2
<=
n
<=
1000
,
m
>=
1
,
∑
k
i
<=
500000
,
1
<=
k
i
<=
m
i
n
(
15
,
n
−
1
)
对于
100%
100
%
的数据,
2<=n<=250000,m>=1,∑ki<=500000,1<=ki<=n−1
2
<=
n
<=
250000
,
m
>=
1
,
∑
k
i
<=
500000
,
1
<=
k
i
<=
n
−
1
分析:
一个很浅显的树形dp,设
f[i]
f
[
i
]
为断掉
i
i
于其子树内所有关键点的代价,显然
特殊的,当 i i 为关键点时,。
因为 ∑ki ∑ k i 比较小,所以我们不一定每一次询问都要跑整棵树一遍。我们可以对原树建一棵虚树,虚树是只保留所有关键点和他们的 lca l c a 的树。
我们维护一个栈,栈中元素表示一条从当前点到根的链。我们先对关键点排序,我们插入一个点时,如果这个点时栈顶元素的儿子,直接加入;否则不断弹出栈顶,知道这个元素是当前元素的父亲。
注意:虚树上的边权为原路径上边权的最小值。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
#define LL long long
const int maxn=250007;
const LL inf=1e16;
using namespace std;
int n,m,x,y,w,cnt,tot,top;
int ls[maxn],dfn[maxn],dep[maxn],a[maxn],pre[maxn];
int f[maxn][20],h[maxn][20];
int q[maxn];
LL ans[maxn];
struct edge{
int y,w,next;
}g[maxn*2];
struct node{
int x,key;
}b[maxn];
void add(int x,int y,int w)
{
g[++cnt]=(edge){y,w,ls[x]};
ls[x]=cnt;
}
void dfs(int x,int fa)
{
dfn[x]=++cnt;
f[x][0]=fa;
dep[x]=dep[fa]+1;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (y==fa) continue;
h[y][0]=g[i].w;
dfs(y,x);
}
}
int lca(int x,int y)
{
if (dep[x]>dep[y]) swap(x,y);
int d=dep[y]-dep[x],k=19,t=1<<k;
while (d)
{
if (d>=t)
{
d-=t;
y=f[y][k];
}
t/=2,k--;
}
if (x==y) return x;
k=19;
while (k>=0)
{
if (f[x][k]!=f[y][k])
{
x=f[x][k];
y=f[y][k];
}
k--;
}
return f[x][0];
}
bool cmp(int x,int y)
{
return dfn[x]<dfn[y];
}
bool cmp1(node x,node y)
{
return dfn[x.x]<dfn[y.x];
}
LL getmin(int x,int y)
{
int d=dep[y]-dep[x],k=19,t=1<<k;
LL minn=inf;
while (d)
{
if (d>=t)
{
d-=t;
minn=min(minn,(LL)h[y][k]);
y=f[y][k];
}
t/=2,k--;
}
return minn;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&w);
add(x,y,w);
add(y,x,w);
}
cnt=0;
dfs(1,0);
for (int j=1;j<20;j++)
{
for (int i=1;i<=n;i++)
{
f[i][j]=f[f[i][j-1]][j-1];
h[i][j]=min(h[i][j-1],h[f[i][j-1]][j-1]);
}
}
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
scanf("%d",&tot);
for (int j=1;j<=tot;j++) scanf("%d",&a[j]);
a[++tot]=1;
sort(a+1,a+tot+1,cmp);
top=0,cnt=0;
q[++top]=a[1];
b[++cnt]=(node){a[1],0};
for (int j=2;j<=tot;j++)
{
b[++cnt]=(node){a[j],1};
int d=lca(q[top],a[j]);
if (d==q[top]) q[++top]=a[j];
else
{
while (dep[q[top-1]]>=dep[d])
{
pre[q[top]]=q[top-1];
top--;
}
if (q[top]!=d)
{
pre[q[top]]=d;
q[top]=d;
b[++cnt]=(node){d,0};
}
q[++top]=a[j];
}
}
while (top>1) pre[q[top]]=q[top-1],top--;
sort(b+1,b+cnt+1,cmp1);
for (int i=1;i<=cnt;i++) ans[b[i].x]=0;
for (int i=cnt;i>0;i--)
{
int x=b[i].x;
if (b[i].key) ans[x]=inf;
ans[pre[x]]+=min(ans[x],(LL)getmin(pre[x],x));
}
printf("%lld\n",ans[1]);
}
}