「解题报告」[luoguP6584]重拳出击 (贪心).md
Posted brucew
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「解题报告」[luoguP6584]重拳出击 (贪心).md相关的知识,希望对你有一定的参考价值。
「解题报告」[luoguP6584]重拳出击 (贪心).md
题面
题意
有一颗大小为 (n) 的边权为 (1) 的无根树, 树上有 (m) 个 Youyou.
初始时, 小 Z 在 (x) 号节点上.
每一回合, 小 Z 可以杀死所有与它距离小于等于 (k) 的 Youyou, 并可以移动到相邻节点或保持不动.
回合结束时, 所有的 Youyou 会向小 Z 的方向移动一条边, 若它与小 Z 在同一节点上, 则不移动.
求小 Z 消灭所有的 Youyou 所需的最小回合数.
数据范围
(1 le n,m le 4 imes 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
",ans+1);
return 0;
}
以上是关于「解题报告」[luoguP6584]重拳出击 (贪心).md的主要内容,如果未能解决你的问题,请参考以下文章
ArrayList 重拳出击,把 LinkedList 干翻在地