题解 P4103 [HEOI2014]大工程
我一直注视着你,似近,似远。但你永远,看不见我……
解题思路
虚树好题
首先,对于总价值比较好求,对于一条边,它对于答案的贡献就是他所联通的两部分选中节点的 \(size\) 的乘积。
然后对于求最小值以及最大值的方法大同小异,因此在这里只介绍求最小值的方法:
我们先要计算出每个节点的子树到该节点距离的最小值以及次小值,分别记为\(mn1\)和\(mn2\)。
剩下的主要分两种情况:
-
对于属于 \(k\) 个节点中一个:直接用\(mn1\)更新全局最小值就好了
-
对于其他的点: 用\(mn1+mn2\)来更新全局最小值。
-
注意事项:
-
对于每一个在计划中的点,到该节点最短的节点一定是自身,\(minn\)搞成0。
mn1=(vis[x]==true)?0:mn1;
-
在处理最大值次大值以及最小值次小值的时候注意更新顺序。
-
在除了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;
}