P6419 [COCI2014-2015#1] Kamp

· · 题解

这是一个 6 个 dfs 加上 LCA 的做法。写完看了一遍题解都是换根 dp 和树剖。

手模一下,第 i 个点的答案可以这样计算,设由这个点和 k 个被选点组成的极小的子树的边权和为 Sum,设点 ik 个点的最长距离为 mx,则:

理解一下就是假若送完 $k$ 个人后要回到起点,那么一进一出,每条边的贡献就要算 $2$ 次。 解除了回到原点的限制,即说明到了最后一个点之后到起点的路径不算贡献,我们当然希望这条边越长越好,因此是最长路径。 考虑实现: - 我们用第一个 dfs 来处理 LCA 需要用到的信息。 - 第二个 dfs 标记由 $k$ 个点形成的 **虚树** 中的点以及边,同时计算虚树上的边权和,记为 $Sum'$。 - 第三个 dfs 跑一下虚树外的点到虚树的距离。记为 $dise_i$。 - 第五个和第六个 dfs 用来计算虚树上的点到被选的 $k$ 个点的最长距离,记为 $disv_i$。具体计算方式是,我们跑第五个dfs时,同时维护由父节点过来的最长距离,到节点 $u$ 时,用第五个 dfs 找向下的最长距离和次长距离。据此求出子节点递归时的维护信息,再往下递归第五个 dfs 即可。 - 第四个 dfs 在上面两个 dfs 的基础上计算虚树外的点到被选的 $k$ 个点的最长距离 $disv_i$。 最后统计答案:

\begin{aligned} ans&=2 \times Sum-mx\ &=2 \times (Sum'+dise_i)-disv_i \end{aligned}

时间复杂度是 $O(n \log n)$,瓶颈在 LCA,精细实现可以达到 $O(n)$。 ```cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll N=5e5+1e2,M=N<<1; ll n,k; ll a[N]; ll h[N],t[M],weigh[M],nex[M],cnt; void add(ll u,ll v,ll w) { ++cnt; t[cnt]=v; weigh[cnt]=w; nex[cnt]=h[u]; h[u]=cnt; } ll Lca,faLca; ll fa[N][20],dep[N],dist[N]; void dfs(ll u,ll f) { if(u==Lca)faLca=f; fa[u][0]=f,dep[u]=dep[f]+1; for(ll i=h[u];i;i=nex[i]) { ll j=t[i]; if(j==f)continue; dist[j]=dist[u]+weigh[i]; dfs(j,u); } } ll lg2[N]; void init() { for(ll i=2;i<=n;i++)lg2[i]=lg2[i>>1]+1; for(ll i=1;i<=lg2[n];i++) for(ll j=1;j<=n;j++) fa[j][i]=fa[fa[j][i-1]][i-1]; } ll lca(ll x,ll y) { if(dep[x]<dep[y])swap(x,y); ll d=dep[x]-dep[y]; while(d) { ll k=lg2[d]; x=fa[x][k]; d-=(1<<k); } if(x==y)return x; ll mx_up=lg2[dep[x]]; for(ll i=mx_up;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } bool used[N]; bool used_e[M]; ll sum; bool dfs2(ll u,ll f) { for(ll i=h[u];i;i=nex[i]) { ll j=t[i]; if(j==f)continue; if(dfs2(j,u)) { used[u]=true; used_e[i]=true; sum+=weigh[i]; } } if(used[u])return true; return false; } ll dis_e[N],dis_v[N]; void dfs3(ll u,ll f) { for(ll i=h[u];i;i=nex[i]) { ll j=t[i]; if(j==f||used[j])continue; dis_e[j]=dis_e[u]+weigh[i]; dfs3(j,u); } } void dfs4(ll u,ll f) { for(ll i=h[u];i;i=nex[i]) { ll j=t[i]; if(j==f||used[j])continue; dis_v[j]=dis_v[u]+weigh[i]; dfs4(j,u); } } ll dfs6(ll u,ll f) { ll res=0; for(ll i=h[u];i;i=nex[i]) { ll j=t[i]; if(j==f||!used[j])continue; res=max(res,dfs6(j,u)+weigh[i]); } return res; } void dfs5(ll u,ll f,ll mx) { ll fir=0,sec=0; for(ll i=h[u];i;i=nex[i]) { ll j=t[i]; if(j==f||!used[j])continue; ll res=dfs6(j,u)+weigh[i]; if(res>fir)sec=fir,fir=res; else if(res>sec)sec=res; } dis_v[u]=max(mx,fir); for(ll i=h[u];i;i=nex[i]) { ll j=t[i]; if(j==f||!used[j])continue; ll tot=dfs6(j,u)+weigh[i]; if(tot==fir) dfs5(j,u,max(mx+weigh[i],sec+weigh[i])); else dfs5(j,u,max(mx+weigh[i],fir+weigh[i])); } } signed main() { memset(dis_e,127,sizeof dis_e); scanf("%lld%lld",&n,&k); for(ll i=1;i<n;i++) { ll x,y,z; scanf("%lld%lld%lld",&x,&y,&z); add(x,y,z),add(y,x,z); } for(ll i=1;i<=k;i++) scanf("%lld",&a[i]),used[a[i]]=true,dis_v[a[i]]=0; dfs(1,0); init(); Lca=a[1]; for(ll i=2;i<=k;i++) Lca=lca(Lca,a[i]); dfs(1,0); dfs2(Lca,faLca); sum<<=1; for(ll i=1;i<=n;i++) if(used[i]) { dis_e[i]=0; dfs3(i,0); } dfs5(a[1],0,0); for(ll i=1;i<=n;i++) if(used[i]) dfs4(i,0); for(ll i=1;i<=n;i++) { ll res=0; res=sum+(dis_e[i]<<1)-dis_v[i]; printf("%lld\n",res); } } ```