luogu P2495
传送门:https://www.luogu.com.cn/problem/P2495
题意:给你一颗n个点的树,现在的情况是每条边有边权,现在要选取一些点,通过删除一些边的方式使得这些点与1号节点失去连通性,删除一条边的代价是边权大小。有多次询问,保证多次询问的总的点数与n同阶。
分析:直接去跑树形dp,显然会gg。因为有多次询问,复杂度会达到惊人的(n*q)。一个直观的想法是,如果我们的复杂度只与要询问的点的数量有关的话,或许就没问题了。直接套上虚树再去跑一个树形dp即可。设dp[i]表示,将i子树中所有选中的点与该点断开的代价。显然dp[i]=dp[i]+min(dp[son[i]],weight[e])(son[i]不是被选中的点),dp[i]=dp[i]+weight[e](son[i]是被选中的点)。
#include<bits/stdc++.h> #define all(x) x.begin(),x.end() #define fi first #define sd second #define lson (nd<<1) #define rson (nd+nd+1) #define PB push_back #define mid (l+r>>1) #define MP make_pair #define SZ(x) (int)x.size() using namespace std; typedef long long LL; typedef vector<int> VI; typedef pair<int,int> PII; inline int read(){ int res=0, f=1;char ch=getchar(); while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();} return res*f; } const int MAXN = 1000005; const int MOD = 1000000007; void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;} int mulmod(int a, int b){return 1ll*a*b%MOD;} template<typename T> void chmin(T& a, T b){if(a>b)a=b;} template<typename T> void chmax(T& a, T b){if(b>a)a=b;} #define go(e,u) for(int e=head[u];e;e=Next[e]) int to[MAXN<<1],Next[MAXN<<1],head[MAXN],tol; LL dis[MAXN<<1]; void add_edge(int u,int v,int val){ Next[++tol]=head[u];to[tol]=v;head[u]=tol;dis[tol]=val; Next[++tol]=head[v];to[tol]=u;head[v]=tol;dis[tol]=val; } #define gov(e,u) for(int e=headv[u];e;e=Nextv[e]) int tov[MAXN<<1],Nextv[MAXN<<1],headv[MAXN],tolv; void add_edgev(int u,int v){ Nextv[++tolv]=headv[u];tov[tolv]=v;headv[u]=tolv; } LL minv[MAXN]; int n, h[MAXN], mark[MAXN]; int dep[MAXN],up[MAXN][25],dfn[MAXN],dfncnt; int st[MAXN], top; bool cmp(int x, int y){ return dfn[x]<dfn[y]; } void dfs(int u,int f){ dfn[u]=++dfncnt; for(int i=0;up[u][i];++i)up[u][i+1]=up[up[u][i]][i]; go(e,u){ int v=to[e]; if(v==f)continue; dep[v]=dep[u]+1;up[v][0]=u; minv[v]=min(minv[u],dis[e]); dfs(v,u); } } int getLCA(int u, int v){ if(dep[u]<dep[v]) swap(u,v); for(int i=20;i>=0;--i){ if(dep[up[u][i]]>=dep[v]){ u=up[u][i]; } } if(u==v)return u; for(int i=20;i>=0;--i){ if(up[u][i]!=up[v][i]){ u=up[u][i]; v=up[v][i]; } } return up[u][0]; } LL dfs1(int u){ LL ret=0;LL s=0; gov(e,u){ int v=tov[e]; s+=dfs1(v); } if(mark[u])ret=minv[u]; else ret=min(minv[u],s); return ret; } int main(){ minv[1]=1e18; n=read(); for(int i=1;i<n;++i){ int u,v,val; u=read();v=read();val=read(); add_edge(u,v,val); } dfs(1,0); int q=read(); while(q--){ int cnt=read(); for(int i=1;i<=cnt;++i){ h[i]=read(); mark[h[i]]=1; } sort(h+1,h+cnt+1,cmp); //建立虚树 st[top=1]=1;tolv=0;headv[1]=0; for(int i=1;i<=cnt;++i){ if(h[i]!=1){ int l=getLCA(h[i],st[top]); if(l!=st[top]){ while(dfn[l]<dfn[st[top-1]]){ add_edgev(st[top-1],st[top]); --top; } if(dfn[l]>dfn[st[top-1]]){ headv[l]=0;add_edgev(l,st[top]);st[top]=l; }else{ add_edgev(l,st[top--]); } } headv[h[i]]=0;st[++top]=h[i]; } } for(int i=1;i<top;++i)add_edgev(st[i],st[i+1]); cout<<dfs1(st[1])<<endl; for(int i=1;i<=cnt;++i)mark[h[i]]=0; } return 0; }