P3523 POI2011 DYN-Dynamite

彬彬冰激凌發表於2024-07-07

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