标签:href 报告 init 距离 code mes return std dfs
有一颗大小为 \(n\) 的边权为 \(1\) 的无根树, 树上有 \(m\) 个 Youyou.
初始时, 小 Z 在 \(x\) 号节点上.
每一回合, 小 Z 可以杀死所有与它距离小于等于 \(k\) 的 Youyou, 并可以移动到相邻节点或保持不动.
回合结束时, 所有的 Youyou 会向小 Z 的方向移动一条边, 若它与小 Z 在同一节点上, 则不移动.
求小 Z 消灭所有的 Youyou 所需的最小回合数.
\(1 \le n,m \le 4 \times 10^5,\ 0 \le k \le 10^6\).
以小 Z 初始位置 x 为根节点
找出深度最深的 Youyou, 记为 y, 记它的深度为 dis. 可以证明 : 处理完 y 后再回到根节点, 所需的天数为 dis. (对 dis 分奇偶讨论一下就能证).
而 dis 天后, 剩余的 Youyou 肯定会与小 Z 相遇. (否则它的深度就大于 dis.)
所以先处理 y 后再处理剩下的 Youyou 可能不会比在根节点等着更劣.
具体做法 : 遍历从 x 到 y 的这条链, 对每个节点算出 "处理完 y 后再去处理它子树中深度次大的点" 的时间, 取个 max, 最后答案即为 "处理 y 所需的时间" +max.
#include<bits/stdc++.h>
using namespace std;
const int _=4e5+7,__=8e5+7,inf=0x3f3f3f3f;
int n,m,X,K,ans,maxn,dis[_],pl;
int lst[_],nxt[__],to[__],tot;
bool b[_],tag;
void _add(int x,int y){
nxt[++tot]=lst[x]; to[tot]=y; lst[x]=tot;
nxt[++tot]=lst[y]; to[tot]=x; lst[y]=tot;
}
void _init(){
cin>>n; int x,y;
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
_add(x,y);
}
cin>>m;
for(int i=1;i<=m;i++){
scanf("%d",&x);
b[x]=1;
}
cin>>K>>X;
}
void _pre(int u,int fa){
dis[u]= b[u] ?0 :-inf;
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
_pre(v,u);
dis[u]=max(dis[u],dis[v]+1);
}
}
void _dfs(int u,int fa,int dep){
int x1=0,x2=0;
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
if(dis[v]>dis[x1]){ x2=x1; x1=v; }
else if(dis[v]>dis[x2]) x2=v;
}
if(x2){
int stp=pl-dep,pth;
if(tag) pth=max(0,dis[x2]-K-dep+1)/2;
else pth=max(0,dis[x2]+1-K-dep+1)/2;
maxn=max(maxn,pth);
}
if(x1&&dep<pl) _dfs(x1,u,dep+1);
}
void _run(){
dis[0]=-1;
ans=max(0,dis[X]+1-K)/2;
pl=max(0,dis[X]-K)/2;
tag=(dis[X]-K)%2;
_dfs(X,0,0);
ans+=maxn;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("x.in","r",stdin);
freopen("x.out","w",stdout);
#endif
_init();
_pre(X,0);
_run();
printf("%d\n",ans+1);
return 0;
}
「解题报告」[luoguP6584]重拳出击 (贪心).md
标签:href 报告 init 距离 code mes return std dfs
原文地址:https://www.cnblogs.com/BruceW/p/13149510.html