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();
}
posted @ 2024-12-06 11:47  liuboom  阅读(35)  评论(0)    收藏  举报