luogu P4654 [CEOI2017]Mousetrap

传送门

这里把终点设为根方便后续处理,那么目标就是要让老鼠走到根

首先考虑老鼠动不了的情况,这种情况下可以把从这个点到终点路径上的分支堵住,然后再疏通路径上的走过的边,可以发现这是这种情况下最优的决策.在这种情况下,老鼠一定是先往上走一段距离(也可能不走),然后走进某个分支的子树,再被赶到终点.

答案显然满足二分性.对于老鼠,如果有一种策略使得花费步数\(>mid\)那么\(mid\)就不合法.所以可以从起点\(start\)往上走,每到一个点看有没有分支满足这个条件.所以还要知道进入一个子树后的花费步数,记为\(f_x\),转移的话,首先应该把\(f\)最大的儿子堵住,然后老鼠走进次大的儿子,剩下的儿子在老鼠被困住时堵住就行了,所以\(f_x=|son\ of\ x|-[|son\ of\ x|>1]+f_{x2}(son\ of\ x=\{x1,x2,...\},f_{x1}>f_{x2}>f_{x3}...)+1\).

如果进入某个分支子树,那么首先对于当前点,要把其他分支堵住,然后要把祖先的分支也堵住,所以再记分支数量\(s_x=\sum_{y\ is\ the\ ancestor\ of\ x,y\neq root} |son\ of\ y|-[y!=start]\),以及走入某个点以后到结束的操作步数\(g_x=f_x+s_{fa_x}-1\).走到某个点,如果有\(g_x>mid\)的儿子,就要尽量消耗能用的操作次数堵住,这个能用次数初始为\(1\),每次往上走一个点就\(+1\).如果还有这样的\(g_x\)就不合法.要注意每次向上走之前要把\(mid\)减去当前层的使用过的操作次数

// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define LL long long

using namespace std;
const int N=1e6+10;
LL rd()
{
    LL x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int to[N<<1],nt[N<<1],hd[N],tot=1;
void add(int x,int y)
{
    ++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
    ++tot,to[tot]=x,nt[tot]=hd[y],hd[y]=tot;
}
int n,rt,fa[N],sz[N],f[N],g[N],ans;
int st[N],tp;
void dfs(int x)
{
    for(int i=hd[x];i;i=nt[i])
    {
        int y=to[i];
        if(y==fa[x]) continue;
        fa[y]=x,dfs(y);
    }
    tp=0;
    for(int i=hd[x];i;i=nt[i])
    {
        int y=to[i];
        if(y==fa[x]) continue;
        st[++tp]=f[y];
    }
    sz[x]=tp;
    sort(st+1,st+tp+1);
    f[x]+=tp-(tp>1)+st[tp-1]+1;
}
bool ck(int mid,int xx)
{
    int cn=1,la=0,dt=0;
    while(xx!=rt)
    {
        st[tp=0]=sz[fa[xx]];
        for(int i=hd[xx];i;i=nt[i])
        {
            int y=to[i];
            if(y==fa[xx]||y==la) continue;
            st[++tp]=g[y];
        }
        st[0]+=tp;
        sort(st+1,st+tp+1);
        while(tp>0&&cn>0&&st[tp]>mid) --tp,--cn,++dt;
        if(st[tp]>mid) return 0;
        mid-=dt,dt=0,la=xx,xx=fa[xx],++cn;
    }
    return 1;
}

int main()
{
    n=rd(),rt=rd();
    int x=rd(),la=0;
    for(int i=1;i<n;++i) add(rd(),rd());
    dfs(rt);
    tp=0;
    int xx=x;
    while(xx!=rt) st[++tp]=xx,xx=fa[xx];
    --sz[st[tp]];
    for(int i=tp-1;i;--i) sz[st[i]]+=sz[st[i+1]]-1;
    ++sz[st[1]];
    sz[rt]=0;
    xx=x;
    while(xx!=rt)
    {
        for(int i=hd[xx];i;i=nt[i])
        {
            int y=to[i];
            if(y==fa[xx]||y==la) continue;
            g[y]=f[y]+sz[xx]-1;
        }
        la=xx,xx=fa[xx];
    }
    int l=0,r=n;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(ck(mid,x)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/smyjr/p/11110880.html
今日推荐