消耗战
首先考虑朴素 \(dp\),设 \(f_u\) 表示使 \(u\) 的子树内的所有关键点都不与 \(u\) 连通的最小代价。如果当前在 \(u\),\(j\) 这个儿子是关键点,那么有转移 \(f_u\leftarrow f_u+val_{u\rightarrow j}\);否则有转移 \(f_u\leftarrow f_u+\min(f_j,val_{u\rightarrow j})\)。
这样做的时间复杂度是 \(O(nm)\) 的,我们考虑优化。
暴力代码:
#include<bits/stdc++.h>
#define int long long
#define N 250005
#define M 500005
using namespace std;
int n,m,h[N],e[M],w[M],ne[M],idx,x[N],f[N];
bool st[N];
void add(int a,int b,int c){
e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
void dfs(int u,int fa){
f[u]=0;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa)continue;
dfs(j,u);
if(st[j])f[u]+=w[i];
else f[u]+=min(f[j],w[i]);
}
}
signed main(){
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);add(b,a,c);
}
cin>>m;
while(m--){
int k;
cin>>k;
for(int i=1;i<=k;i++){
cin>>x[i];
st[x[i]]=1;
}
dfs(1,0);
cout<<f[1]<<'\n';
for(int i=1;i<=k;i++){
st[x[i]]=0;
}
}
return 0;
}
发现 \(\sum k\) 非常小,那么我们能不能把 \(dp\) 的总复杂度控制在 \(O(\sum k)\) 呢?
我们接下来说明如何建虚树:
首先加入所有关键点,然后把他们按照 \(dfs\) 序排序,接着加入相邻两点的 \(lca\),最后对这些点去重,建树。首先这样所有询问的总点数不超过 \(2\times \sum k\),所以复杂度是对的。
那么,为什么这样做是对的呢?考虑两个 \(dfs\) 序相邻的点 \(x,y\) 他们的 \(lca\) 为 \(fa\)。于是我们只需要连接 \(fa\rightarrow y\)。这样是不重不漏的。分类讨论一下:
-
\(x\) 是 \(y\) 的祖先,则 \(fa\) 为 \(x\),因为 \(x,y\) 的 \(dfs\) 序相邻,所以 \(x\rightarrow y\) 上没有其他关键点。
-
\(x\) 不是 \(y\) 的祖先,那么把 \(fa\) 当作 \(y\) 的祖先,\(fa\rightarrow y\) 同上可得没有关键点。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 250005
#define K 20
#define pii pair<int,int>
#define x first
#define y second
using namespace std;
int n,m,k,h[N],a[N],cnt,mn[N];
int dfn[N],tot,f[N];
int fa[N][K],dep[N];
bool st[N];
vector<pii>g[N];
vector<int>e[N];
void add(int a,int b){
e[a].push_back(b);
}
void add1(int a,int b,int c){
g[a].push_back({b,c});
}
void dfs(int u,int pre){
fa[u][0]=pre;
for(int i=1;i<K;i++){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
dfn[u]=++tot;
dep[u]=dep[pre]+1;
for(auto it:g[u]){
int j=it.x,val=it.y;
if(j==pre)continue;
mn[j]=min(mn[u],val);
dfs(j,u);
}
}
int get_lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
for(int i=K-1;~i;i--){
if(dep[fa[a][i]]>=dep[b]){
a=fa[a][i];
}
}
if(a==b)return a;
for(int i=K-1;~i;i--){
if(fa[a][i]!=fa[b][i]){
a=fa[a][i];
b=fa[b][i];
}
}
return fa[a][0];
}
int dp(int u){
if(e[u].size()==0)return mn[u];
int sum=0;
for(auto j:e[u]){
sum+=dp(j);
}
e[u].clear();
if(st[u])return mn[u];
else return min(sum,mn[u]);
}
void build(){
sort(h+1,h+k+1,[&](int x,int y){
return dfn[x]<dfn[y];
});
for(int i=1;i<k;i++){
a[++cnt]=h[i];
a[++cnt]=get_lca(h[i],h[i+1]);
}
a[++cnt]=h[k];
a[++cnt]=1;
sort(a+1,a+cnt+1,[&](int x,int y){
return dfn[x]<dfn[y];
});
cnt=unique(a+1,a+cnt+1)-a-1;
for(int i=1;i<cnt;i++){
int lca=get_lca(a[i],a[i+1]);
add(lca,a[i+1]);
}
}
signed main(){
cin>>n;
memset(mn,0x3f,sizeof mn);
for(int i=1;i<n;i++){
int a,b,c;
cin>>a>>b>>c;
add1(a,b,c);add1(b,a,c);
}
dfs(1,0);
cin>>m;
while(m--){
cin>>k;
for(int i=1;i<=k;i++){
cin>>h[i];
st[h[i]]=1;
}
cnt=0;
build();
cout<<dp(1)<<'\n';
for(int i=1;i<=k;i++){
st[h[i]]=0;
}
}
return 0;
}
标签:return,int,void,dfs,fa,虚树,define
From: https://www.cnblogs.com/zxh923aoao/p/18404060