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);
}