题意
- 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
1≤n≤5×105
s ≠ t s\ne t s=t
思路
- 首先我们可以去获得
s
∼
t
s\sim t
s∼t 这一条链,去单独拿出来
然后可以获得 ∀ 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=l−1+maxd[l] ,因为我们 l − 1 l-1 l−1 是步长; m a x r = c n t − r + m a x d [ r ] maxr=cnt-r+maxd[r] maxr=cnt−r+maxd[r]
那么我们目前的答案 a n x = m a x l − m a x r anx=maxl-maxr anx=maxl−maxr - 然后开始倒着走。先是
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,cnt−l+maxd[l]}
然后更新我们的答案, s s s 走到了 l − 1 l-1 l−1,此时 s s s 的值就是 ( l − 1 − 1 ) + m a x d [ l − 1 ] (l-1-1)+maxd[l-1] (l−1−1)+maxd[l−1] ,此时 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,(l−1−1)+maxd[l−1]−maxr}
然后我们更新 m a x l maxl maxl ,多了一个可以走的位置 l − 1 l-1 l−1 ,也可以去更新 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,(l−1−1)+maxd[l−1]} - 然后就到 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;
}