虚树学习笔记 & luogu P2495 [SDOI2011]消耗战

背景:

我好菜(连虚树都不会,虽然曾经比赛时出现过,但现在才学)。
看了几个某 J J 中学同级认识的大佬的 b l o g blog ,感觉被 d i s s diss 爆了。
感觉自己好菜 . . . ...

题目传送门:

https://www.luogu.org/problemnew/show/P2495

题意:

一棵树,多组询问,每一次选出 k k 个点,使得 1 1 无法到这 k k 个点所删边的权值和的最小值。

思路:

虚树的入门题。
参考:https://www.cnblogs.com/zwfymqz/p/9175152.html
虚树:只用保留所必需的点(被询问到的点和它们的 l c a lca )。
构建过程:
在这里插入图片描述
但是我认为那个大佬的 d f n dfn 并不是 d f s dfs 序,而是重链剖分的顺序(树剖的顺序),然后代码就跟他不同了。
最后写个显然的 d p dp 即可。
其实细节挺复杂的。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
	struct node1{int x,y,next;LL z;} a[600010];
	struct node2{int x,y,next;}b[600010];
	int lasta[600010],lastb[600010],d[600010],id[600010],f[600010][25],sta[600010],dep[600010];
	LL mi[600010],dp[600010];
	int n,m,k,lena=0,lenb=0;
void ins_a(int x,int y,LL z)
{
	a[++lena]=(node1){x,y,lasta[x],z}; lasta[x]=lena;
}
void ins_b(int x,int y)
{
	b[++lenb]=(node2){x,y,lastb[x]}; lastb[x]=lenb;
}
void init()
{
	for(int i=1;i<=20;i++)
		for(int j=1;j<=n;j++)
			f[j][i]=f[f[j][i-1]][i-1];
}
int op=0;
void dfs(int x,int fa)
{
	id[x]=++op;
	for(int i=lasta[x];i;i=a[i].next)
	{
		int y=a[i].y;
		if(y==fa) continue;
		f[y][0]=x;
		mi[y]=min(mi[x],a[i].z);
		dep[y]=dep[x]+1;
		dfs(y,x);
	}
}
int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[y]<=dep[f[x][i]]) x=f[x][i];
	if(x==y) return x;
	for(int i=20;i>=0;i--)
		if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
int top;
void build(int x)
{
	int LCA=lca(x,sta[top]);
	if(sta[top]!=LCA)
	{
		while(id[sta[--top]]>id[LCA]) ins_b(sta[top],sta[top+1]),ins_b(sta[top+1],sta[top]);
		ins_b(LCA,sta[top+1]),ins_b(sta[top+1],LCA);
		if(LCA!=sta[top]) sta[++top]=LCA;
	}
	sta[++top]=x;
}
void solve(int x,int fa)
{
	dp[x]=mi[x];
	LL sum=0;
	for(int i=lastb[x];i;i=b[i].next)
	{
		int y=b[i].y;
		if(y==fa) continue;
		solve(y,x);
		sum+=dp[y];
	}
	lastb[x]=0;
	if(sum) dp[x]=min(sum,(LL)mi[x]);
}
bool cmp(int x,int y)
{
	return id[x]<id[y];
}
int main()
{
	int x,y;
	LL z;
	scanf("%d",&n);
	mi[1]=(1ll<<60);
	for(int i=1;i<n;i++)
	{
		scanf("%d %d %lld",&x,&y,&z);
		ins_a(x,y,z),ins_a(y,x,z);
	}
	dep[1]=1;
	dfs(1,0);
	init();
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&k);
		for(int j=1;j<=k;j++)
			scanf("%d",&d[j]);
		sort(d+1,d+k+1,cmp);
		int op=1;
		for(int j=2;j<=k;j++)
			if(lca(d[op],d[j])!=d[op]) d[++op]=d[j];
		k=op;
		sta[top=1]=1;
		lenb=0;
		for(int j=1;j<=k;j++)
			build(d[j]);
		top--;
		while(top)
			ins_b(sta[top],sta[top+1]),ins_b(sta[top+1],sta[top]),top--;
		solve(1,0);
		printf("%lld\n",dp[1]);
	}
}

猜你喜欢

转载自blog.csdn.net/zsyz_ZZY/article/details/89219296