P2495 [SDOI2011] 消耗战
P2495 [SDOI2011] 消耗战
题意简述:
在一颗树上,每次给定\(k\)个关键点,求切断1到这些点的最小花费的总和(最小化总花费)
我们考虑dp:记 \(dp[x]\)为切断\(dp[x]\)内的所有点到1的路径的最小花费
显然我们有两种切法,一种是对每个子树下的\(dp[to]\) 求和,另一种是记录一个\(mincut[x]\),表示1->x这段路上的所有边中最小的边权
但这样的时间复杂度是O(nq)的,显然不行
那我们该怎么办呢?
虚树闪亮登场:
虚树,顾名思义是一颗虚构的树
它只记录了关键节点,在本题中,注意到
$ \sum k_i \leq 5\times 10^5$
所以我们考虑建立一颗树,上面只包含关键点和他们的lca
虚树的建立:
参考dalao题解:
Link
只要我们建立了虚树,然后直接对每个虚树结点进行树上dp然后我们这题就做完了
记得每次做完之后清空数组和之前虚树建的边!!!
Code
#include<bits/stdc++.h>
#define int long long
const int N=5e5+5;
const int inf=1e17;
const int lg=20;
using namespace std;
int n,m,e1_cnt,e2_cnt,dfn_cnt,top;
int head1[N],head2[N],mincut[N],dfn[N],que[N],tag[N];
int dep[N],st[N];
int f[N][lg+5];
struct Edge{
int to,nxt,w;
}e1[N<<1],e2[N<<1];
void add(int x,int y,int w,Edge e[],int head[],int &cnt)
{
e[++cnt]={y,head[x],w};
head[x]=cnt;
}
void dfs1(int x)
{
dfn[x]=++dfn_cnt;
for(int i=1;i<=lg;i++)
{
f[x][i]=f[f[x][i-1]][i-1];
}
for(int i=head1[x],to,w;i;i=e1[i].nxt)
{
to=e1[i].to,w=e1[i].w;
if(!dfn[to])
{
dep[to]=dep[x]+1;
mincut[to]=min(mincut[x],w);
f[to][0]=x;
dfs1(to);
}
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=lg;i>=0;i--)
{
if(dep[y]<=dep[f[x][i]])x=f[x][i];
}
if(x==y)return x;
for(int i=lg;~i;i--)
{
if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
}
return f[x][0];
}
int dfs2(int x)
{
int sum=0,res=inf;
for(int i=head2[x],to;i;i=e2[i].nxt)
{
to=e2[i].to;
sum+=dfs2(to);
}
if(tag[x])res=mincut[x];
else res=min(mincut[x],sum);
tag[x]=0;
head2[x]=0;
return res;
}
bool cmp(int x,int y)
{
return dfn[x]<dfn[y];
}
void solve()
{
int k;
scanf("%lld",&k);
for(int i=1;i<=k;i++)
{
scanf("%lld",&que[i]);
tag[que[i]]=1;
}
sort(que+1,que+1+k,cmp);
top=1;
st[top]=que[1];
for(int i=2;i<=k;i++)
{
int now=que[i];
int lca=LCA(now,st[top]);
while(1)
{
if(dep[lca]>=dep[st[top-1]]) //case1 or case2 or case3
{
if(lca!=st[top])//case2 or case 3
{
add(lca,st[top],0,e2,head2,e2_cnt);
if(lca!=st[top-1])//case2
{
st[top]=lca;//st.pop(top) and st.push(lca)
}
else //case3
{
top--;
}
}
// else : case 1
break;
}
else//case 4
{
add(st[top-1],st[top],0,e2,head2,e2_cnt);
top--;
}
}
st[++top]=now;//case 1 2 3 4
}
while(--top)
{
add(st[top],st[top+1],0,e2,head2,e2_cnt);
}
int ans=dfs2(st[1]);
printf("%lld\n",ans);
e2_cnt=1;
}
void work()
{
mincut[1]=inf;
cin>>n;
for(int i=1,x,y,w;i<n;i++)
{
scanf("%lld%lld%lld",&x,&y,&w);
add(x,y,w,e1,head1,e1_cnt);
add(y,x,w,e1,head1,e1_cnt);
}
dfs1(1);
cin>>m;
for(int i=1;i<=m;i++)
{
solve();
}
}
#undef int
int main()
{
//freopen("P2495.in","r",stdin);//freopen("P2495.out","w",stdout);
work();
}

浙公网安备 33010602011771号