1. 程式人生 > 其它 >21牛客多校J - Tree(思維,rmq)

21牛客多校J - Tree(思維,rmq)

題目連結

J-Tree_2021牛客暑期多校訓練營8 (nowcoder.com)

題解

將Toilet-Ares簡稱為\(\rm T\),將Unidentified-person簡稱為\(\rm U\)

\(s\)​到\(t\)​的路徑中,如果離開路徑,那麼玩家能走的最遠距離就是他走進的子樹的最遠距離。以\(t\)​為根構建有根樹,很容易就可以預處理出在\(s\)​到\(t\)​​的路徑上一個點離開後能走的最遠距離。

假設陣列\(a[i]\)​​​代表\(\rm T\)​​從\(s\)出發在\(i\)​​點離開的能走最遠距離,陣列\(b[i]\)​代表\(\rm U\)​從\(t\)出發在\(i\)

​​點離開能走的最遠距離,路徑範圍為\([1, {\rm len}]\)​​。如果當前\(\rm T\)​先手在\(l\)​​點,\(\rm U\)​在\(r\)​​點,\(\rm T\)​離開路徑,那麼\(\rm U\)​就會選擇選擇\(\max\limits_{l<i\le r}(b[i])\)​​,此時差值為\(a[l]-\max\limits_{l<i\le r}(b[i])\)​​​,\(\rm U\)​先手離開路徑也同理。

所以如果一方離開路徑,馬上就可以知道結果;否則由於兩方都沒有離開路徑,所以可以列舉\(\rm T\)\(\rm U\)所在位置,因為他們是輪流走的,複雜度是線性的,按照以下策略統計答案:

  • 如果當前\(\rm T\)離開路徑差值嚴格大於下一步\(\rm U\)離開路徑的差值,那麼可以結束迴圈,因為\(\rm U\)會盡可能讓差值小,如果\(\rm T\)不離開,\(\rm U\)至少會在下一步離開使得差值比現在\(\rm T\)​​​離開路徑差值變小;
  • 否則,假設當前\(\rm T\)離開路徑的差值為\(d_{\rm T}\),接下來\(\rm U\)離開路徑的差值為\(d_{\rm U}\)。由於\(\rm T\)要差值最大,會有\(ans_{\rm T}=\max(ans_{\rm T}, d_T)\)\(\rm U\)要差值最小,有\(ans_{\rm U}=\max(ans_{\rm U}, d_U)\)
    。如果想在這一步之前結束,有\(ans=\max(ans_{\rm T}, ans_{\rm U})\)
  • 繼續列舉下一步\(\rm T\)\(\rm U\)的位置,最終答案為\(ans\)
#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 1e6 + 10;
const int M = 1e6 + 10;
const double eps = 1e-5;

struct edge {
    int ntp, ne;
} ed[M];
int head[N];
int si;

void add(int u, int v) {
    si++;
    ed[si] = {v, head[u]};
    head[u] = si;    
}


int up[N];
int mxlen[N];
int s, t;
int dfs(int p, int fa, int d) {
    up[p] = fa;
    int mxd = d;
    for(int e = head[p]; e; e = ed[e].ne) {
        int ntp = ed[e].ntp;
        if(ntp == fa) continue;
        mxd = max(mxd, dfs(ntp, p, d + 1));
    }
    mxlen[p] = mxd - d + 1;
    return mxd;
}



int len[N];
int premx[26][N], lasmx[26][N];

int que(int l, int r, int arr[][N]) {
    int k = log2(r - l + 1);
    return max(arr[k][l], arr[k][r-(1<<k)+1]);

}

int main() {
    
    IOS;
    int n;
    cin >> n >> s >> t;
    for(int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        add(v, u);
        add(u, v);
    }
    dfs(t, 0, 1);
    int cur = s;
    int pre = 0;
    int cnt = 0;
    while(cur) {
        int num = 0;
        for(int e = head[cur]; e; e = ed[e].ne) {
            int ntp = ed[e].ntp;
            if(ntp == pre || ntp == up[cur]) continue;
            num = max(mxlen[ntp], num);
        }
        len[++cnt] = num;
        pre = cur;
        cur = up[cur];
    }
    for(int i = 1; i <= cnt; i++) {
        premx[0][i] = len[i] + i - 1;
    }
    for(int i = cnt; i >= 1; i--) {
        lasmx[0][i] = len[i] + cnt - i;
    }
    for(int i = 1; i < 25; i++) {
        for(int j = 1; j + (1 << i) - 1 <= cnt; j++) {
            premx[i][j] = max(premx[i - 1][j], premx[i - 1][j + (1 << (i - 1))]);
            lasmx[i][j] = max(lasmx[i - 1][j], lasmx[i - 1][j + (1 << (i - 1))]);
        }
    }
    int ans1 = -INF;
    int ans2 = INF;
    int ans = -INF;
    int l = 1, r = cnt;
    while(l < r) {
        ans1 = max(ans1, premx[0][l] - que(l + 1, r, lasmx));
        ans = max(ans, min(ans1, ans2));
        if(l + 1 < r - 1) {
            if(premx[0][l] - que(l + 1, r, lasmx) > que(l + 1, r - 1, premx) - lasmx[0][r]) break;
            ans2 = min(ans2, que(l + 1, r - 1, premx) - lasmx[0][r]);
        }
        l++, r--;
    }
    if(l == r) {
        ans1 = max(ans1, premx[0][l] - lasmx[0][l + 1]);
    }
    ans = max(ans, min(ans1, ans2));
    cout << ans << endl;
}