CodeForces Round 898 (div 4)
H. Mad City
大致思路
- 對於有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;
}