P3523 POI2011 DYN-Dynamite

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\) 为二分的距离):

  1. \(f[u]>mid\)\(u\) 为关键点。

    \(g[u]=max(g[u],0)\)

    解释:显而易见,当前点可以作为一个没有被覆盖的关键点。

  2. \(g[u]+f[u]\leq mid\)。​

    \(g[u]=-\infty\)

    解释:显然没有一个没被覆盖的关键点,易证 \(g[u]\)\(f[u]\) 不来自一棵子树。

  3. \(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);
}
posted @ 2024-07-07 12:58  彬彬冰激凌  阅读(12)  评论(0)    收藏  举报