P3523 POI2011 DYN-Dynamite
小 trick,加双倍经验。
思路
使 \(dis\) 的最大值最小,可以想到二分 \(dis\),然后根据 \(dis\) 判断可行性。
那么可以把问题转化为,所有关键点到选择的点的距离小于 \(dis\) 的前提下,使得使用的点的个数最小。
这就是:P3942 将军令
考虑设 \(f[u]\) 为距离 \(u\) 最近的选中的点的距离,\(g[u]\) 为距离 \(u\) 最远的未被覆盖的关键点的距离。
这里覆盖指:对于一个关键点,到最近的被选中点的距离小于等于当前二分的 \(dis\)。
有朴素转移:
\[f[u]=\max(f[u],f[v]+1)\\
g[u]=\max(g[u],g[v]+1)
\]
然后要分讨一下(下文中的 \(mid\) 为二分的距离):
-
当 \(f[u]>mid\) 且 \(u\) 为关键点。
\(g[u]=max(g[u],0)\)。
解释:显而易见,当前点可以作为一个没有被覆盖的关键点。
-
当 \(g[u]+f[u]\leq mid\)。
\(g[u]=-\infty\)。
解释:显然没有一个没被覆盖的关键点,易证 \(g[u]\) 和 \(f[u]\) 不来自一棵子树。
-
当 \(g[u]=mid\)。
\(g[u]=-\infty,f[u]=0\) 且需选择的点个数加一。
解释:该点必须被选择。
最后判断一下根的 \(g[u]\) 是否大于 \(0\),如果大于 \(0\) 在加一个选中的点。
然后套上二分就 OK 了。
CODE
#include<bits/stdc++.h>
using namespace std;const int maxn=3e5+5;struct Edge
{int tot;int head[maxn];struct edgenode{int to,nxt;}edge[maxn*2];inline void add(int x,int y){tot++;edge[tot].to=y;edge[tot].nxt=head[x];head[x]=tot;}
}T;int n,m;
int a[maxn];int g[maxn],f[maxn];int gs;
inline void dfs(int u,int fa,int mid)
{g[u]=-1e9,f[u]=1e9;for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(v==fa) continue;dfs(v,u,mid);f[u]=min(f[v]+1,f[u]);g[u]=max(g[v]+1,g[u]);}if(f[u]>mid&&a[u]) g[u]=max(g[u],0);if(f[u]+g[u]<=mid) g[u]=-1e9;if(g[u]==mid) f[u]=0,g[u]=-1e9,gs++;
}
inline void solve(int mid)
{gs=0;dfs(1,0,mid);gs+=(g[1]>=0);printf("%d",gs);
}int main()
{int t;scanf("%d%d%d",&n,&m,&t);for(int i=1;i<=n;i++) a[i]=1;for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);T.add(u,v),T.add(v,u);}solve(m);
}