前言
本来一个很直观的题面,非要搞形式化题意反而使题意变得非常迷惑。
题意
有一栋树形建筑,其中有一些点摆放了 TNT ,树边上都摆放了引信,引信的燃烧时间为 \(1\) 秒 \(/\) 边,现在你要选择 \(m\) 个点同时点燃引信(起爆),则显然 TNT 被引爆的时间为到离它最近的起爆处的距离,请你求出所有 TNT 爆炸所需的最短时间。
思路
首先最大值最小肯定是二分,check(mid)
就是求至少需要同时在多少点起爆才能做到 \(mid\) 秒内所有 TNT 引爆,如果这个值大于 \(m\) 则说明时间不够,小于 \(m\) 说明时间还可以更短。当然 check
不只有这一种,但个人认为这是相对容易的方式。
那么可以考虑树形 dp(严格来讲是贪心),记 \(f[x][0]\) 为 \(x\) 子树中离 \(x\) 最远的还不能被引爆的** TNT 距离,\(f[x][1]\) 为 \(x\) 子树中离 \(x\) 最近的起爆点**距离。
-
从儿子转移到父亲是容易的,直接 \(f[u][0]=\max\{f[v][0\}+1\),\(f[u][1]=\min\{f[v][1]\}+1\)。
-
如果 \(u\) 自己就是 TNT ,那么更新 \(f[u][0]\)。
-
如果 \(f[u][0]+f[u][1]\leq mid\),说明 \(u\) 子树中的某个起爆点可以在 \(mid\) 秒内,通过经过 \(u\) 的引信引爆 \(u\) 子树中最远的 TNT ,那既然最远的都能引爆,说明 \(u\) 子树中所有的 TNT 都能被引爆,于是 \(f[u][0]=-\infin\)。
-
如果离 \(u\) 最远的 TNT 距离等于 \(mid\) 了,那么就必须在 \(u\) 设置起爆点,更新 \(f[u][0]\),\(f[u][1]\)。
这样贪心一定是优的,因为我们每个 TNT 都是拖到实在不能再拖的时候才起爆的。
代码
#include<bits/stdc++.h>
#define getmax(x,y) x=max(x,y)
#define getmin(x,y) x=min(x,y)
#define getmid int mid=(l+r)/2
using namespace std;
#ifdef ONLINE_JUDGE
#define getchar __getchar
inline char __getchar(){
static char ch[1<<20],*l,*r;
return (l==r&&(r=(l=ch)+fread(ch,1,1<<20,stdin),l==r))?EOF:*l++;
}
#endif
template<class T>inline void rd(T &x){
T res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=getchar();}
while('0'<=ch && ch<='9'){res=res*10+ch-'0';ch=getchar();}
x=res*f;
}
template<class T>inline void wt(T x,char endch='\0'){
static char wtbuff[20];
static int wtptr;
if(x==0){
putchar('0');
}
else{
if(x<0){x=-x;putchar('-');}
wtptr=0;
while(x){wtbuff[wtptr++]=x%10+'0';x/=10;}
while(wtptr--) putchar(wtbuff[wtptr]);
}
if(endch!='\0') putchar(endch);
}
const int MAXN=300005,INF=0x3f3f3f3f;
int n,m,f[MAXN][2],cnt,head[MAXN],ecnt=0;
bitset<MAXN>charge;
struct EDGE{
int v,nxt;
}e[MAXN<<1];
inline void add(const int& u,const int& v){
e[++ecnt].v=v;
e[ecnt].nxt=head[u];
head[u]=ecnt;
}
void dfs(int x,int fa,int val){
f[x][0]=-INF,f[x][1]=INF;
for(int i=head[x];i;i=e[i].nxt){
int it=e[i].v;
if(it==fa) continue;
dfs(it,x,val);
getmax(f[x][0],f[it][0]+1);
getmin(f[x][1],f[it][1]+1);
}
if(charge[x]) getmax(f[x][0],0);
if(f[x][0]+f[x][1]<=val) f[x][0]=-INF;
if(f[x][0]==val){
cnt++;
f[x][0]=-INF;f[x][1]=0;
}
}
inline bool check(int x){
cnt=0;
dfs(1,0,x);
cnt+=(f[1][0]>=0);
return cnt<=m;
}
int main(){
rd(n);rd(m);
for(int i=1,x;i<=n;i++){
rd(x);
charge[i]=x;
}
for(int i=1,u,v;i<n;i++){
rd(u);rd(v);
add(u,v);add(v,u);
}
int l=0,r=n,res=n-1;
while(l<=r){
getmid;
if(check(mid)){
res=mid;
r=mid-1;
}
else{
l=mid+1;
}
}
cout<<res<<endl;
return 0;
}
标签:TNT,ch,P3523,POI2011,mid,char,DYN,引爆,getchar
From: https://www.cnblogs.com/SkyNet-PKN/p/18517405