题解 P4103 [HEOI2014]大工程

\(\huge LCA模板\)

我一直注视着你,似近,似远。但你永远,看不见我……

解题思路

虚树好题

首先,对于总价值比较好求,对于一条边,它对于答案的贡献就是他所联通的两部分选中节点的 \(size\) 的乘积。

然后对于求最小值以及最大值的方法大同小异,因此在这里只介绍求最小值的方法:

我们先要计算出每个节点的子树到该节点距离的最小值以及次小值,分别记为\(mn1\)\(mn2\)

剩下的主要分两种情况:

  1. 对于属于 \(k\) 个节点中一个:直接用\(mn1\)更新全局最小值就好了

  2. 对于其他的点: 用\(mn1+mn2\)来更新全局最小值。

  • 注意事项:

    1. 对于每一个在计划中的点,到该节点最短的节点一定是自身,\(minn\)搞成0。mn1=(vis[x]==true)?0:mn1;

    2. 在处理最大值次大值以及最小值次小值的时候注意更新顺序。

    3. 在除了Lougu的OJ上用倍增会超时,改成树链剖分或者RMQ就好了。

code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e6+10,M=N<<1,INF=1e18;
int n,m,tim,T,top,root,rtdep,sta[N],s[N],id[M][21],lg[M],dfn[N];
ll sum,minn,maxn,dep[N];
bool vis[N];
struct Edge
{
	int tot,head[N],nxt[M],ver[M];
	void add(int x,int y)
	{
		ver[++tot]=y;
		nxt[tot]=head[x];
		head[x]=tot;
	}
}e1,e2;
struct Node
{
	int siz,mn,mx;
};
inline void dfs(int x,int fa)
{
	dfn[x]=++tim;
	dep[x]=dep[fa]+1;
	id[tim][0]=x;
	for(int i=e1.head[x];i;i=e1.nxt[i])
		if(e1.ver[i]!=fa)
		{
			dfs(e1.ver[i],x);
			id[++tim][0]=x;
		}
}
inline int LCA(int x,int y)
{
	x=dfn[x],y=dfn[y];
	if(x>y)
		swap(x,y);
	int k=lg[y-x+1];
	return (dep[id[x][k]]<dep[id[y-(1<<k)+1][k]])?id[x][k]:id[y-(1<<k)+1][k];
}
inline bool comp(int x,int y)
{
	return dfn[x]<dfn[y];
}
inline void judge(int x)
{
	if(dep[x]<rtdep)
	{
		rtdep=dep[x];
		root=x;
	}
}
inline void build(int x)
{
	if(!top)
	{
		sta[++top]=x;
		judge(x);
		return ;
	}
	int lca=LCA(x,sta[top]);
	while(top>1&&dep[sta[top-1]]>dep[lca])
	{
		e2.add(sta[top-1],sta[top]);
		top--;
	}
	if(dep[lca]<dep[sta[top]])
		e2.add(lca,sta[top--]);
	if(!top||sta[top]!=lca)
	{
		sta[++top]=lca;
		judge(lca);
	}
	sta[++top]=x;
	judge(x);
}
inline void RMQ_init()
{
	for(int i=2;i<=tim;i++)
		lg[i]=lg[i>>1]+1;
	for(int j=1;j<=20;j++)
		for(int i=1;(i+(1<<j)-1)<=tim;i++)
		{
			int num=i+(1<<(j-1));
			id[i][j]=(dep[id[i][j-1]]<dep[id[num][j-1]])?id[i][j-1]:id[num][j-1];
		}
}
inline Node redfs(int x,int fa)
{
	if(!e2.head[x])
	{
		vis[x]=false;
		return (Node){1,0,0};
	}
	ll siz=0,mn1=INF,mn2=INF,mx1=0,mx2=0;
	Node ans;
	for(int i=e2.head[x];i;i=e2.nxt[i])
	{
		ll dist=dep[e2.ver[i]]-dep[x];
		ans=redfs(e2.ver[i],x);
		sum+=dist*ans.siz*(m-ans.siz);
		siz+=ans.siz;
		ll tmp1=ans.mn+dist,tmp2=ans.mx+dist;
		if(mn1>=tmp1)
		{
			mn2=mn1;
			mn1=tmp1;
		}
		else	mn2=min(mn2,tmp1);
		if(mx1<=tmp2)
		{
			mx2=mx1;
			mx1=tmp2;
		}
		else	mx2=max(mx2,tmp2);
	}
	minn=min(minn,mn1+mn2);
	maxn=max(maxn,mx1+mx2);
	if(vis[x])
	{
		minn=min(minn,mn1);
		maxn=max(maxn,mx1);
	}
	siz+=(vis[x]==true);
	mn1=(vis[x]==true)?0:mn1;
	vis[x]=e2.head[x]=0;
	return (Node){siz,mn1,mx1};
}
int main()
{
	scanf("%d",&n);
	for(int i=1,x,y;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		e1.add(x,y);
		e1.add(y,x);
	}
	dfs(1,0);
	RMQ_init();
	scanf("%d",&T);
	while(T--)
	{
		root=1;
		rtdep=N;
		sum=0,minn=INF,maxn=0;
		top=e2.tot=0;
		scanf("%d",&m);
		for(int i=1;i<=m;i++)
		{
			scanf("%d",&s[i]);
			vis[s[i]]=true;
		}
		sort(s+1,s+m+1,comp);
		for(int i=1;i<=m;i++)
			build(s[i]);
		while(--top)
			e2.add(sta[top],sta[top+1]);
		redfs(root,0);
		printf("%lld %lld %lld\n",sum,minn,maxn);
	}
	return 0;
}
posted @ 2021-07-01 21:34  Varuxn  阅读(54)  评论(0编辑  收藏  举报