給出一棵根爲1的樹,樹邊有邊權
php
有m個詢問,每一個詢問輸入k個點的編號,求出在使得這k個點不能與根節點連通的狀況下,總共要刪掉的邊權的總和最小,求出最小總邊權html
假如只有一個詢問,那麼咱們能夠直接dpnode
對於樹上的點x,轉移分爲兩種狀況spa
1.斷開本身與父親的聯繫,代價爲從根到該節點的最小值code
2.不考慮該節點(前提是該節點不是詢問點),把子樹內的全部詢問點都斷開的代價htm
可是若是有m個詢問的話,逐個逐個DP的複雜度顯然O(mn)會爆炸blog
有一個地方能夠做爲突破口的就是Σk<=500000get
就要用虛樹來作了,詳細請左轉string
並且這題在構造的時候要注意,有兩個詢問點x,y,y在以x爲根的子樹內,就不把y建到虛樹中,由於咱們只要分割了x,y天然就被分割了it
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<vector> #define Maxn 310000 using namespace std; typedef long long LL; struct node{int x,y,next;LL d;}a[Maxn*2];int len,last[Maxn]; void ins(int x,int y,LL d){a[++len]=(node){x,y,last[x],d};last[x]=len;} vector<int> v[Maxn]; int dfn[Maxn],id,dep[Maxn];LL mn[Maxn]; int f[Maxn][21]; int LCA(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--) if(dep[x]-dep[y]>=(1<<i)) x=f[x][i]; if(x==y) return x; for(int i=20;i>=0;i--) { if(dep[x]>=(1<<i)&&f[x][i]!=f[y][i]) { x=f[x][i];y=f[y][i]; } } return f[x][0]; } void dfs(int x,int fa) { dfn[x]=++id; for(int i=1;dep[x]>=(1<<i);i++) f[x][i]=f[f[x][i-1]][i-1]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y==f[x][0]) continue; mn[y]=min(mn[x],a[k].d); f[y][0]=x;dep[y]=dep[x]+1; dfs(y,x); } } int p[Maxn]; bool cmp(int x,int y){return dfn[x]<dfn[y];} int sta[Maxn],tp; void add(int x) { if(tp==1){sta[++tp]=x;return ;} int lca=LCA(x,sta[tp]); if(lca==sta[tp]) return ;//由於阻隔了上面的點就能阻隔下面的點 while(tp>1&&dfn[sta[tp-1]]>=dfn[lca]) v[sta[tp-1]].push_back(sta[tp]),tp--; if(lca!=sta[tp]) v[lca].push_back(sta[tp]),sta[tp]=lca; sta[++tp]=x; } LL dp(int x) { if(v[x].size()==0) return mn[x]; LL d=0; for(int i=0;i<v[x].size();i++) { int y=v[x][i]; d+=dp(y); } v[x].clear(); return min(d,mn[x]); } int main() { int n; scanf("%d",&n); len=0;memset(last,0,sizeof(last)); for(int i=1;i<n;i++) { int x,y,d; scanf("%d%d%d",&x,&y,&d); ins(x,y,d);ins(y,x,d); } memset(mn,63,sizeof(mn)); id=dep[1]=0;dfs(1,0); int m; scanf("%d",&m); while(m--) { int k; scanf("%d",&k); for(int i=1;i<=k;i++) scanf("%d",&p[i]); sort(p+1,p+k+1,cmp); tp=0;sta[++tp]=1; for(int i=1;i<=k;i++) add(p[i]); while(tp!=0) v[sta[tp-1]].push_back(sta[tp]),tp--; printf("%lld\n",dp(1)); } return 0; }