問題の説明
戦争においては、n-1からなるN島と橋によって戦場、各2つの島の間の唯一の経路アップがされていることを確認します。今、私たちの軍は島のための第1で敵の本部を検出することがあり、そして、彼らは戦い、目の前で軍事的勝利を維持するのに十分なエネルギーを持っていません。他の島kに豊富なエネルギーを有することが知られて、エネルギーへの敵のアクセスを防止するために、私たちの軍の任務の一部は、敵がどのエネルギーが豊富な島に到達できないように、橋を爆破することです。異なる材料とブリッジの異なる構造に、別のを爆破するためのブリッジでは、総コストを最小化するために目標を達成しながら、私たちの軍を願って、異なる価格を持っています。
調査部門はまた、謎の敵機があることがわかりました。軍はすべてのエネルギーを遮断した後も、彼らはまた、そのマシンを使用することができます。機械生成の効果が橋を爆破するために私たちの軍の全てを解決しないだろう、とリソースの配分ランダムに再します(ただし、リソースが島に第1号に配布されていないことを保証することができます)。しかし、調査はまた、部門がこれだけのマシンをm回使用できることがわかったので、私たちはただ、各タスクを完了する必要があります。
入力形式
最初の行の整数nは、島の数を表します。
次に、3つの整数、U、VのN-1株は、W、Uはその<= U、V <= N-1と<=確保するために、島の数および島ブリッジ番号v cは直接考慮することによって連結されて表しますC <= 100000。
N + 1行目、整数mは、敵機の代表的な数を使用することができます。
次のm行、各整数KI、i番目の代わりに、KIアイランドが豊富な資源、次の整数kのH1、H2を有し、... HKは、資源が豊富な島の数を表します。
出力フォーマット
これは、タスクごとの最小コストを表し、m本の出力線を有します。
サンプル入力
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 7 8 3
3 4 9 6
サンプル出力
12
32
22
データ範囲
データの100%、2 <= N <= 250000、M> = 1、シグマ(KI)<= 500000,1 <= KI <= N-1まで
解決
仮想ツリー・ボードの問題のすべての......まず、動的なプログラミングを行う方法を検討します。いずれかの点が明らかに、エネルギーポイントと通信しないように、エッジの最小数を削除するために、ポイントのために、すべてのポイントまたはポイントかどうかは、親ノードに接続されているように、それが、ポイントに接続されていないサブツリー。したがって、ノード\(U \)最小コストノードが生成される\(MIN(SUM、W(U、V))\) (ここで、\(SUM \)は、第1の実施形態のコストを表し、\(V \)を表します\(U \)親ノードの)。特殊なケースでは、ノードは、エネルギーのノードであり、次いで、最小コストツリーのサブツリーのために第2の方式の価格であることです。
ダイナミック計画ので、うまく仮想ツリーでは、次の最適化を考えました。各点のエネルギー及びキーポイントとしてのLCAは、元のツリーに似たツリー構造を再確立が、唯一のキーポイントを保持します。この新しいツリーは、仮想ツリーと呼ばれています。必要とされる最小、2つのツリー間の最短経路の右側に一次仮想ツリーの右サイドに。しかし、これはまた、時間がそれを最適化する方法はありません、爆発を直接2点間のパスを必要としますか?
仮想ツリーを考慮2つの隣接ノード\(U、V \)と\(U \)親\(F \) 、元のツリー\(F \)する(Vの\)を\介して結合経路が\(U \) 、次いで場合\(Fは\)する(Uを\)\最小値未満である\(U \)する(V \)\最小値を、明らかに壊れ\(W(U、F) \ )さらに好ましく、そうでなければ破壊((U、V、W)\ \) より良いです。したがって、あなたは答えをする必要があります見つけることができます(V \)\し(F \)\上の最小値を。広いプッシュ、ポイントの右側の父親は答えには影響しませんルート・パス上の最小値に直接割り当てることができます。
コード
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define N 500002
using namespace std;
int head[N],ver[2*N],nxt[2*N],l;
int n,m,i,j,d[N],size[N],son[N],fa[N],top[N],dfn[N],k,t,cnt,s[2*N],h[2*N];
long long dis[N],edge[N*2];
bool e[N];
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
void insert(int x,int y,long long z)
{
l++;
ver[l]=y;
edge[l]=z;
nxt[l]=head[x];
head[x]=l;
}
void dfs1(int x,int pre,int dep)
{
fa[x]=pre;size[x]=1;
dfn[x]=++cnt;d[x]=dep;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y!=pre){
dis[y]=min(dis[x],edge[i]);
dfs1(y,x,dep+1);
size[x]+=size[y];
if(size[y]>size[son[x]]) son[x]=y;
}
}
}
void dfs2(int x,int y)
{
top[x]=y;
if(son[x]) dfs2(son[x],y);
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(!top[y]) dfs2(y,y);
}
}
int LCA(int x,int y)
{
if(x==0||y==0) return 0;
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]]) swap(x,y);
x=fa[top[x]];
}
if(d[x]<d[y]) swap(x,y);
return y;
}
int cmp(const int &x,const int &y)
{
return dfn[x]<dfn[y];
}
long long dp(int u,int pre)
{
if(e[u]) return dis[u];
long long sum = 0;
for(int i = head[u]; i; i = nxt[i]) if(ver[i]!=pre) sum += dp(ver[i],u);
return min(sum, dis[u]);
}
int main()
{
cin>>n;
for(i=1;i<n;i++){
int u,v,w;
u=read();v=read();w=read();
insert(u,v,w);insert(v,u,w);
}
dis[1]=1LL<<50;
dfs1(1,0,0);
dfs2(1,0);
cin>>m;
for(i=1;i<=m;i++){
memset(e,0,sizeof(bool)*(n+5));
memset(head,0,sizeof(int)*(n+5));
l=t=0;
k=read();
for(j=1;j<=k;j++) h[j]=read(),e[h[j]]=1;
sort(h+1,h+k+1,cmp);
for(j=1;j<k;j++) h[j+k]=LCA(h[j],h[j+1]);
k*=2;h[k]=1;
sort(h+1,h+k+1,cmp);
k=unique(h+1,h+k+1)-h-1;
for(j=1;j<=k;j++){
while(t&&dfn[s[t]]+size[s[t]]-1<dfn[h[j]]) t--;
if(t){
insert(s[t],h[j],dis[h[j]]);
insert(h[j],s[t],dis[h[j]]);
}
s[++t]=h[j];
}
printf("%lld\n",dp(1,0));
}
return 0;
}
//P.S. 树链剖分找LCA跑的真快......