1. 程式人生 > >[2018.5.24集訓]山景城-博弈論

[2018.5.24集訓]山景城-博弈論

題解 初始 clas 一次 div ++ return 博弈 標記

題目大意

絕頂聰明的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();
}

[2018.5.24集訓]山景城-博弈論