调用了一下小猴子的思路。。。
概念:
给出一棵树.每次询问选择一些点,求一些东西.这些东西的特点是,许多未选择的点可以通过某种方式剔除而不影响最终结果.
于是就有了建虚树这个技巧.....
我们可以用log级别的时间求出点对间的lca....
那么,对于每个询问我们根据原树的信息重新建树,这棵树中要尽量少地包含未选择节点. 这棵树就叫做虚树.
接下来所说的"树"均指虚树,原来那棵树叫做"原树".
构建过程如下:
按照原树的dfs序号(记为dfn)递增顺序遍历选择的节点. 每次遍历节点都把这个节点插到树上.
首先虚树一定要有一个根. 随便扯一个不会成为询问点的点作根.(并不觉得是这样)
维护一个栈,它表示在我们已经(用之前的那些点)构建完毕的虚树上,以最后一个插入的点为端点的DFS链.
设最后插入的点为p(就是栈顶的点),当前遍历到的点为x.我们想把x插入到我们已经构建的树上去.
求出lca(p,x),记为lca.有两种情况:
1.p和x分立在lca的两棵子树下.
2.lca是p.
(为什么lca不能是x?
因为如果lca是x,说明dfn(lca)=dfn(x)<dfn(a),而我们是按照dfs序号遍历的,于是dfn(a)<dfn(x),矛盾.)
对于第二种情况,直接在栈中插入节点x即可,不要连接任何边(后面会说为什么).
对于第一种情况,要仔细分析.
我们是按照dfs序号遍历的(因为很重要所以多说几遍......),有dfn(x)>dfn(p)>dfn(lca).
这说明什么呢? 说明一件很重要的事:我们已经把lca所引领的子树中,p所在的子树全部遍历完了!
简略的证明:如果没有遍历完,那么肯定有一个未加入的点h,满足dfn(h)<dfn(x),
我们按照dfs序号递增顺序遍历的话,应该把h加进来了才能考虑x.
这样,我们就直接构建lca引领的,p所在的那个子树. 我们在退栈的时候构建子树.
p所在的子树如果还有其它部分,它一定在之前就构建好了(所有退栈的点都已经被正确地连入树中了),就剩那条链.
如何正确地把p到lca那部分连进去呢?
设栈顶的节点为p,栈顶第二个节点为q.
重复以下操作:
如果dfn(q)>dfn(lca),可以直接连边q->p,然后退一次栈. --> while()!!!
【 以q为根的子树在连q->p后构建完毕,接下来构建以lca为根的子树内x及其他dfs序较大的部分 】
如果dfn(q)=dfn(lca),说明q=lca,直接连边lca->p,[退一次栈]【此时子树已经构建完毕】.
【 以p为根的子树在连q(lca)->p后构建完毕,接下来构建以q为根的子树内x及dfs序较大的部分 】
如果dfn(q)<dfn(lca),说明lca被p与q夹在中间,此时连边lca->[p],退一次栈,再把lca压入栈.【此时子树构建完毕】.
【 以p为根的子树在连lca->p后构建完毕,lca为q连接p及x的交叉点需保留,接下来构建以lca为根的子树中x及dfs序较大的部分 】
如果不理解这样操作的缘由可以画画图.....
最后,为了维护dfs链,要把x压入栈. 整个过程就是这样.....
【栈:维护右链】
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
inline int read(void){
int x=0,f=1; char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+ch-'0',ch=getchar());
return x*f;
}
typedef long long LL;
const LL maxn=500009;
LL n,m,tot,top,cnt,z;
LL head[maxn],q[maxn],dfn[maxn],c[maxn];
LL num[maxn],p[maxn],mi[maxn][29],f[maxn];
LL fa[maxn][29],st[maxn][29],pw[29];
bool b[maxn];
struct LJM{
LL to,next,v;
}a[maxn];
inline void add(LL f,LL t,LL w){
a[++tot].to=t; a[tot].v=w;
a[tot].next=head[f]; head[f]=tot;
a[++tot].to=f; a[tot].v=w;
a[tot].next=head[t]; head[t].tot;
}
void dfs(LL x,LL ffa){
dfn[x]=++cnt;
num[cnt]=x;
st[++z][0]=cnt;
p[x]=z;
for(LL i=head[x];i!=-1;i=a[i].next)
if(a[i].to!=ffa)
fa[a[i].to][0]=x,mi[a[i].to][0]=a[i].v,
dfs(a[i].to,x),st[++z][0]=st[p[x]][0];
}
inline void poww(void){
for(LL j=1;j<=20;j++)
for(LL i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1],
mi[i][j]=min(mi[i][j-1],mi[fa[i][j-1]][j-1]);
}
inline void rmp(void){
for(LL j=1;j<=20;j++)
for(LL i=1;i<=z;i++)
st[i][j]=min(st[i][j-1],st[i+pw[j-1]][j-1]); //Look out to make three times larger memory
}
inline bool cmp(const LL&x,const LL&y){
return dfn[x]<dfn[y];
}
inline LL findmin(LL x,LL tar){
LL ans=1e9;
for(LL i=20;i>=0;i--)
if(dfn[fa[x][i]]>=dfn[tar])
ans=min(ans,mi[x][i]),x=fa[x][i];
// if(ans==1e9) puts("CNM");
return ans;
}
inline LL dp(LL x,LL fa){
for(LL i=head[x];i!=-1;i=a[i].next)
if(a[i].to!=fa){ //head[x]!=-1 !!!
dp(a[i].to,x);
f[x]+=b[a[i].to]?a[i].v:min(a[i].v,f[a[i].to]); //!!!
}
if(!f[x]) f[x]=(LL)1e16;
}
inline void clear(LL x,LL fa){
for(LL i=head[x];i!=-1;i=a[i].next)
if(a[i].to!=fa) clear(a[i].to,x);
head[x]=-1; b[x]=f[x]=0;
}
int main(int argc,char const *argv[]){
pw[0]=1;
for(LL i=1;i<=20;i++)
pw[i]=pw[i-1]<<1;
memset(head,-1,sizeof(head));
n=read();
for(LL i=1,fr,t,w;i<n;i++){
fr=read(),t=read(),w=read();
add(fr,t,w);
}
dfs(1,0); poww(); rmq(); m=read();
memset(head,-1,sizeof(head));
for(LL i=1,k;i<=m;i++){
tot=0; top=0; k=read();
for(LL j=1;j<=k;j++)
c[j]=read(),b[c[j]]=1;
sort(c+1,c+k+1,cmp);
q[++top]=1; q[++top]=c[1];
for(LL j=2;j<=k;j++){
if(lca(c[j],q[top])==q[top]) q[++top]=c[j];
else{
while(dfn[q[top-1]]>dfn[lca(c[j],q[top])]) //while!!!
add(q[top-1],q[top],findmin(q[top],q[top-1])),top--;
if(dfn[q[top-1]]==dfn[lca(c[j],q[top])])
add(q[top-1],q[top],findmin(q[top],q[top-1])),top--;
else{
q[top+1]=lca(c[j],q[top]);
add(q[top+1],q[top],findmin(q[top],q[top+1]));
q[top]=q[top+1];
}
q[++top]=c[j];
}
}
while(top>1)
add(q[top-1],q[top],findmin(q[top],q[top-1])),top--;
dp(1,0); printf("%lld\n",f[1]); clear(1,0);
}
return 0;
}