CodeForces Round 898 (div 4) H題解析

熙玺發表於2024-07-17

CodeForces Round 898 (div 4)
H. Mad City

CodeForces Round 898 (div 4) H題解析

CodeForces Round 898 (div 4) H題解析

大致思路

  • 對於有n條邊和n個點,說明這個圖裡面只有一個環
  • 並且兩人同時開始和結束移動,所以可以得到當Valeriu進入到這個圖裡面的唯一的環裡面時,Marcel就無法再抓到他,我們可以把離Valeriu最近的入環點叫做KeyPoint,所以我們就需要考慮Valeriu是否能在Marcel趕到KeyPoint之前趕到KeyPoint
  • 所以找到入環點是這道題的切入點,找到入環點,我們就可以透過dfs暴搜來得出他們和入環點的距離來,透過比較來得出是否能夠逃離

Key:入環點的尋找

思路來源於洛谷的_Ink大佬

方法是透過拓撲,因為在拓撲的過程中,會一步步去刪點刪邊,最後只剩下一個環。因為是要找離逃離者最近的入環點,所以我們可以把一開始的KeyPoint設為b點,即要逃離者所在地點,當 Keypoint 所在的點將被刪時,由拓撲排序的特性,此時僅有一條邊與其相連,所以我們可以在刪這個點時順勢把KeyPoint 值更新到與被刪點相連的那個點上。

由於拓撲排序不會刪掉環上的點,所以當 KeyPoint 值不停更新,直到更新到環上的點時就不再發生變化。此時,KeyPoint 值就是我們想要的那個點的編號了。

這道題的圖為無向圖,所以拓撲過程中的刪點刪邊條件應該為入度為1,這樣環上的點就永遠不會被刪除。

完整程式碼

#include <bits/stdc++.h>
using namespace std;
const int N = 200060, M = N << 1;
int h[N], ne[M], e[M], idx;
int n, a, b, keypoint;
int to[N];//記錄一個點的入度
bool vist[N], st[N];
int dista, distb;//記錄a,b離關鍵點的距離
void add(int a, int b)//鏈式前向星式建圖
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
void topsort(int dot)//對入度為1的點進行拓撲
{
    vist[dot] = true;//把這個點標記,表示在此次拓撲中已經被訪問
    for (int i = h[dot]; ~i; i = ne[i])
    {
        int j = e[i];
        if (vist[j] || to[j]==0)continue;//如果這個點已經被訪問過,就跳過
        //如果這個點已經被刪除也跳過
        to[dot]--;
        to[j]--;//因為是雙向的道路,刪邊需要兩個點的入度都減1
        if (dot == keypoint)keypoint = j;//刪點,如果這個點是關鍵點就轉移關鍵點
        if (to[j] == 1)topsort(j);//對於入度為1的點進行拓撲
    }
    vist[dot] = false;//恢復狀態
}
void dfs(int x, int dist)//dfs暴搜求a和b到關鍵點的距離
{
    st[x] = true;//標記點已經訪問
    if (x == a)dista = min(dista, dist);
    if (x == b)distb = min(distb, dist);
    for (int i = h[x]; ~i; i = ne[i])
    {
        int j = e[i];
        if (st[j])continue;//如果點已經被訪問就跳過
        dfs(j, dist + 1);
    }
    st[x] = false;//恢復現場
}
void init()//對於每一次的狀態的初始化
{
    memset(h, -1, sizeof h);
    idx = 0;
    memset(to, 0, sizeof to);
    memset(st, 0, sizeof st);
    memset(vist, 0, sizeof vist);
    dista = distb = 0x3f3f3f3f;
}
void solve()
{
    cin >> n >> a >> b;
    init();
    keypoint = b;
    for(int i=1;i<=n;i++)
    {
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
        to[u]++;
        to[v]++;//因為是雙向道路,所以兩個點的入度都+1
    }
    for (int i = 1; i <= n; i++)if (to[i] == 1)topsort(i);
    //找到入度為1的點進行拓撲
    if (to[b] >= 2 && a != b)//如果一開始b就在環上,而且a沒有和b在同一棟大樓,就說明可以無期限逃
    {
        cout << "YES" << endl;
        return;
    }
    else if (a == b) {//如果a和b在同一棟大樓,就直接被抓了
        cout << "NO" << endl;
        return;
    }
    dfs(keypoint, 0);//DFS查詢a和b兩點離關鍵點的距離
    
    //如果a到關鍵點的距離大於b到關鍵點的距離,說明b可以比a先到環上,所以可以無期限逃,反之不可以
    if (dista > distb)cout << "YES" << endl;
    else cout << "NO" << endl;
}
int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

相關文章