[2018.5.24集训]山景城-博弈论

题目大意

绝顶聪明的A和B在一棵树上博弈。

A的目标是进行最少次数的操作,使B到达节点t。每回合A可以进行三种决策:
1.不操作(这不算操作次数) 2.切断一条树上的边 3.消除一条边上B留下的标记。

B初始位于节点s,目标是在到达t之前使A进行尽可能多的操作。
每回合,若B所处节点存在没有被切断也没有留下标记的边,则B必须从这些边之中选择一条走过去,并在这条边上做标记,否则原地不动。

求A的最终操作次数。

部分分:s和t之间有一条边。

题解

假定令t为树根。

首先考虑部分分。
观察可知,最后B一定会走到一个死路,然后A在封锁B所在节点到t上的其他边后,沿路进行消除标记操作强行让B到达t。
并且,A在B走到死路之前进行的消除标记操作是不优的,会导致B的下一步的选择变多,即使不会改变最优答案,将来再消除和现在消除的总次数也是一样的,该切断的边也还是要切,不如优先假定消除标记操作全部在最后进行。

然后考虑一个dp:设 $f_i$ 代表B第一次来到 $i$ 节点,并被A强制送回 $i$ 节点这段时间内,A的总操作次数。

那么显然每次A会选择切掉当前点和子树中 $f$ 值最大的点相连的边,而B会选择次大值向下走。
同时,在将B引到当前节点时,应确保当前节点周围的边除了通往t的边和B到达这个位置的边之外的边全部被切断。因此有转移方程:

$$f_u = deg_u-2 + \mathrm{second max}_{v \in child_u}{f_v}$$

最后得到的 $f_s$ 即为部分分答案!

对于原问题,令可以发现B多出了操作:向上走,而A不能切断s到t之间的边,否则B将无法到达t。
于是只能让B随意往上走,但一旦中途B选择进入某个儿子,B下一次在链上就是被A强制移动了。

首先对这条链上的每个节点进行一次dp,接下来的操作包括这次dp均不考虑这些点在s-t链上的儿子。
在dp结束后,给当前点的每个儿子的 $f$ 值加上它到t路径上所节点的儿子数量,因为要防止B再次进入另一个儿子从而带来负收益。
因此,现在每个链上节点的儿子的 $f$ 值,代表了若B选择进入当前儿子后,剩余需要完成目标的花费。

直接对链dp不太好做,那么考虑二分答案。
考虑如何判断一个答案是否合法:
从s到t依次枚举链上的节点,令s为1号节点,则在B到达 $i$ 号节点并做出下一步决策前,$A$ 可以有 $i$ 次操作机会。

假如当前二分值为 $lim$。
设 $x$ 为为了使答案合法,目前为止至少需要使用的操作次数。
对于节点 $i$ 的儿子 $v$,若 $x+f_v>lim$,则在B在 $i$ 节点上并还未做出下一步决策之前,$x$ 与 $v$ 之间的边必须切断,否则若B选择进入 $v$ ,答案将不合法。

于是,判定条件为,在扫完 $i$ 的所有儿子后,$x$ 加上 $i$ 需要切断的儿子数,若 $x>i$ 或 $x>lim$,则 $lim$ 不合法。

于是二分一发就完成了~

代码:

#include<cstdio>
#include<vector>
#include<cstdlib>
#include<algorithm>
using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

const int N=1e6+9;

int n,s,t,p,las;
vector<int> g[N];
int f[N],fa[N];
int stk[N],top;

inline void dfs(int u)
{
    for(int i=0,v;i<g[u].size();i++)
        if((v=g[u][i])!=fa[u])
        {
            fa[v]=u;
            dfs(v);
        }
}

inline void dfs2(int u)
{   
    if(!g[u].size())
    {
        f[u]=0;
        return;
    }
    int mx1=0,mx2=0;
    for(int i=0;i<g[u].size();i++)
    {
        dfs2(g[u][i]);
        if(f[g[u][i]]>=mx1)
            mx2=mx1,mx1=f[g[u][i]];
        else if(f[g[u][i]]>mx2)
            mx2=f[g[u][i]];
    }
    f[u]=mx2+g[u].size();
}

namespace faq
{
    inline bool check(int x)
    {
        int must=0;
        for(int i=1;i<=top;i++)
        {
            int cnt=0;
            for(int j=0;j<g[stk[i]].size();j++)
                if(f[g[stk[i]][j]]+must>x)
                    cnt++;
            must+=cnt;
            if(must>i || must>x)return 0;
        }
        return 1;
    }

    int mina()
    {
        dfs2(s);
        stk[top=1]=s;
        for(int x=fa[s],las=s;x!=t;las=x,x=fa[x])
        {
            stk[++top]=x;
            g[x].erase(find(g[x].begin(),g[x].end(),las));
            dfs2(x);
        }

        int tot=0;
        for(int i=top;i>=1;i--)
        {
            tot+=g[stk[i]].size();
            for(int j=0;j<g[stk[i]].size();j++)
                f[g[stk[i]][j]]+=tot;
        }

        int l=0,r=n,mid,ans=n;
        while(l<=r)
        {
            mid=l+r>>1;
            if(check(mid))
                ans=mid,r=mid-1;
            else
                l=mid+1;
        }

        printf("%d\n",ans);
        return 0;
    }
}

int main()
{
    n=read();t=read();s=read();
    for(int i=2,u,v;i<=n;i++)
    {
        u=read();v=read();
        g[u].push_back(v);
        g[v].push_back(u);
    }

    if(s==t)return puts("0"),0;

    dfs(t);return 0;
    for(int i=1;i<=n;i++)
        if(i!=t)g[i].erase(find(g[i].begin(),g[i].end(),fa[i]));

    return faq::mina();
}

猜你喜欢

转载自www.cnblogs.com/zltttt/p/9091238.html