【训练题61:树上博弈 | 数据结构】Tree | 2021牛客暑期多校训练营8

题意

  • Tree | 2021牛客暑期多校训练营8
    给定一颗 n n n 个节点的数,一开始 A l i c e Alice Alice s s s 节点, B o b Bob Bob t t t 节点
    然后 A l i c e Alice Alice 开始,每个人轮流操作
    一次操作,ta 走到当前节点的相邻节点
    要求这个节点两个人之前都没有到达过,若两个人都走不了了就结束
    两个人都做最优决策。
    问, A l i c e Alice Alice 走的节点个数 减去 B o b Bob Bob 走的节点个数 的最大值 是多少?
    即, A l i c e Alice Alice 希望增大这个差, B o b Bob Bob 希望减小这个差
  • 1 ≤ n ≤ 5 × 1 0 5 1\le n\le 5\times10^5 1n5×105
    s ≠ t s\ne t s=t

思路

  • 首先我们可以去获得 s ∼ t s\sim t st 这一条链,去单独拿出来
    然后可以获得 ∀ i ( i 在 链 上 ) , m a x d [ i ] \forall i(i在链上),maxd[i] i(i)maxd[i] 表示从这个点 i i i 出发,且不经过链的其他节点的最大深度。
    容易想到,如果某个人走一步之后脱离这条链,那么 ta 一定会去再多经过 m a x d [ i ] maxd[i] maxd[i] ,让 ta 经过的节点个数尽可能的多,且此时双方不会再有碰撞抢位置的情况发生
  • 现在关键是,我们怎么去确定,两个人分别怎么走是最优的?
    请添加图片描述
  • 假设对于左边的人 ( 后 面 简 称 s ) (后面简称 s) (s) 走第二个点然后再脱离这个链,那么对于右边的人 ( 后 面 简 称 t ) (后面简称 t ) (t) ta 就可以选第三个点到最后一个点的任意一个点,然后再去获得 m a x d [ i ] maxd[i] maxd[i]
    请添加图片描述
  • 在比如,虽然 m a x d [ 2 ] maxd[2] maxd[2] 特别大,但是如果 s s s 走到第四个点,这样 t t t 能选的方案就更少了,有可能导致对第一个人来说更优的解
    所以我们就对于 s s s 来说,枚举所有的路,查看差最大是多少? 这 样 明 显 不 合 理 \color{red}{这样明显不合理}
    比如 m a x d [ 6 ] maxd[6] maxd[6] 特别大,但是 t t t 明显不会让 s s s 走到这里,它就直接把路挡住之类的,就取不到了
    那只枚举那一半 s s s 一定能到达的路? 这 样 明 显 也 不 合 理 \color{red}{这样明显也不合理}
    s s s 当然有机会走到 t t t 的那半边去,只不过 t t t 在别的地方有更优的路所以放过了 s s s 罢了
  • 然后发现,正着做貌似挺复杂的,我们不妨 倒 着 做 \color{green}{倒着做}
    请添加图片描述
  • 首先我们记录 l l l 表示 s s s主动走到的最右边的节点,此时 r = l + 1 r=l+1 r=l+1 明显就是 t t t主动走到的最左边的节点
    (主动:这边的节点的 m a x d [ x ] maxd[x] maxd[x] 很大,要去抢,而不是对方不要然后你从一堆小的中选一个的被动走)
    根据链长,我们便可以得知最后一步是 s s s 走的还是 t t t 走的。我们按图中,假设是 s s s 走的,走到 l l l
    那么我们记录 m a x l maxl maxl m a x r maxr maxr 表示目前 s s s t t t 能最多走几次。假设我们链上有 c n t cnt cnt 个节点
    因为我们假设一开始他们走到头然后再走链的儿子,所以初始 m a x l = l − 1 + m a x d [ l ] maxl=l-1+maxd[l] maxl=l1+maxd[l] ,因为我们 l − 1 l-1 l1 是步长; m a x r = c n t − r + m a x d [ r ] maxr=cnt-r+maxd[r] maxr=cntr+maxd[r]
    那么我们目前的答案 a n x = m a x l − m a x r anx=maxl-maxr anx=maxlmaxr
  • 然后开始倒着走。先是 s s s l l l 左走一步。既然 s s s 左走一步了,那么对于 t t t 来说,它就能到达 l l l 这个位置了,自然我们可以更新 m a x r = max ⁡ { m a x r , c n t − l + m a x d [ l ] } maxr=\max\{maxr,cnt-l+maxd[l]\} maxr=max{maxr,cntl+maxd[l]}
    然后更新我们的答案, s s s 走到了 l − 1 l-1 l1,此时 s s s 的值就是 ( l − 1 − 1 ) + m a x d [ l − 1 ] (l-1-1)+maxd[l-1] (l11)+maxd[l1] ,此时 t t t 的值就是 m a x r maxr maxr ,就获得了新的 a n s = max ⁡ { a n s , ( l − 1 − 1 ) + m a x d [ l − 1 ] − m a x r } ans=\max\{ans,(l-1-1)+maxd[l-1]-maxr\} ans=max{ans,(l11)+maxd[l1]maxr}
    然后我们更新 m a x l maxl maxl ,多了一个可以走的位置 l − 1 l-1 l1 ,也可以去更新 m a x l = max ⁡ { m a x l , ( l − 1 − 1 ) + m a x d [ l − 1 ] } maxl=\max\{maxl,(l-1-1)+maxd[l-1]\} maxl=max{maxl,(l11)+maxd[l1]}
  • 然后就到 t t t 走了,操作大体等价,唯一不同的地方就是对于 t t t 来说,它希望 a n s ans ans 越小越好,此时更新的值为 a n x = min ⁡ { a n s , s o m e t h i n g } anx=\min\{ans,something\} anx=min{ans,something}
  • 终点操作,就是两个人都跑到了原点 s s s t t t ,然后就跳出循环,即可获得答案
    可以看一下操作图来理解一下
    请添加图片描述
  • 看着和之前的没有什么差别?看这个:
    因为对于 t t t 来说,当 s s s 选了第四个节点之后,它只能从这几个节点去选了,因此是和我们最开始想的思路是有点差别的。
    请添加图片描述

代码

  • 时间复杂度: O ( N ) O(N) O(N)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 5e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

vector<int>V[MAX];
bool col[MAX];
int dis[MAX],dis3[MAX];
int fa[MAX],fa2[MAX];

void dfs0(int x,int f){
    fa2[x] = f;
    for(auto it : V[x]){
        if(it == f)continue;
        dfs0(it,x);
    }
}

void dfs1(int x,int f){
    fa[x] = f;
    for(auto it : V[x]){
        if(it == f)continue;
        dis[it] = dis[x] + 1;
        dfs1(it,x);
    }
}
void dye(int x,int y){		// 给中间的链染个色
    int t = y;
    while(t != x){
        col[t] = 1;
        t = fa[t];
    }
    col[x] = 1;
}

void dfs2(int x,int f,int o,int de){		// 其中 dis3[x] 就是 maxd[x] ,每个点不走链上的点能到达的最多借点数
    dis3[o] = max(dis3[o],de);
    for(auto it : V[x]){
        if(it == f)continue;
        if(col[it])continue;
        dfs2(it,x,o,de+1);
    }
}

void getDis3(int s,int t){
    int x = s;
    int l = 0;
    while(x != t){
        dfs2(x,x,x,0);
        x = fa2[x];
        l++;
    }
    dfs2(t,t,t,0);
}
int aa[MAX];
void getAns(int s,int t){		// 获取答案
    int x = s;
    int cnt = 0;
    while(x != t){				// 把链上的值都拿出来放在数组里
        aa[++cnt] = dis3[x];
        x = fa2[x];
    }
    aa[++cnt] = dis3[x];
    int now = cnt % 2;
    int l = cnt - cnt / 2;		// s 最右走到 l 的位置
    int r = l + 1;				// t 最左走到 r 的位置
    int mxl = (l - 1) + aa[l];
    int mxr = (cnt - r) + aa[r];
    int ans = mxl - mxr;
    while(1){
        if(now){    /// A unmoves
            mxr = max(mxr,(cnt - l) + aa[l]);
            ans = max(ans,l - 1 - 1 + aa[l-1] - mxr);
            l--;mxl = max(mxl,l-1+aa[l]);
        }else{      /// B unmoves   
            mxl = max(mxl,r - 1 + aa[r]);
            ans = min(ans,mxl - ((cnt-(r+1)) + aa[r+1]));
            r++;mxr = max(mxr,(cnt-r)+aa[r]);
        }
        now ^= 1;
        if(l == 1 && r == cnt)break;
    }
    Print(ans);
    Write();


}

int main()
{
    int n,s,t;
    n = read();s = read();t = read();
    for(int i = 1;i < n;++i){
        int ta,tb;ta = read();tb = read();
        V[ta].push_back(tb);
        V[tb].push_back(ta);
    }
    dfs0(t,t);
    dfs1(s,s);
    dye(s,t);
    getDis3(s,t);
    getAns(s,t);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值