1. 程式人生 > 實用技巧 >[PTA]L2-013 紅色警報

[PTA]L2-013 紅色警報

粗略看了一下網上其他題解,都是暴力跑的,也就是對於每個詢問重建並查集,這樣其實時間複雜度是$O(n^2logn)$的,十分不優秀。

其實有更好的解法,就是時間倒流法,倒序處理每個詢問,每個把刪去一個點刪邊改成加上一個點加邊,一遍並查集即可。

每次判斷是否合併了兩個以上的連通塊。

注意一條邊可用當且僅當兩端的點都存在。

 1 #include <bits/stdc++.h>
 2 #define Mid ((l + r) >> 1)
 3 #define lson (rt << 1)
 4 #define rson (rt << 1 | 1)
 5
using namespace std; 6 int read(){ 7 char c; int num, f = 1; 8 while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; 9 while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; 10 return f * num; 11 } 12 const int N = 509; 13 int n, m, pre[N], a[N], f[N], vis[N]; 14 vector<int
> ver[N]; 15 int fid(int x) { 16 return pre[x] == x ? x : (pre[x] = fid(pre[x])); 17 } 18 signed main() 19 { 20 n = read(); m = read(); 21 for(int i = 1; i <= m; i++) { 22 int x = read() + 1, y = read() + 1; 23 ver[x].push_back(y); 24 ver[y].push_back(x); 25 }
26 for(int i = 1; i <= n; i++) pre[i] = i; 27 int q = read(); 28 for(int i = 1; i <= n; i++) vis[i] = 1; 29 for(int i = 1; i <= q; i++) vis[a[i] = read() + 1] = 0; 30 for(int x = 1; x <= n; x++) if(vis[x]) 31 for(auto j : ver[x]) 32 if(vis[j] && fid(j) != fid(x)) 33 pre[fid(j)] = fid(x); 34 for(int i = q; i; i--) { 35 int ff = 0; 36 for(auto j : ver[a[i]]) { 37 if(vis[j] && fid(j) != fid(a[i])) { 38 pre[fid(j)] = fid(a[i]); 39 ff++; 40 } 41 } 42 vis[a[i]] = 1; 43 if(ff > 1) f[i] = 1; 44 } 45 for(int i = 1; i <= q; i++) { 46 if(f[i]) printf("Red Alert: City %d is lost!\n", a[i] - 1); 47 else printf("City %d is lost.\n", a[i] - 1); 48 } 49 if(q == n) { 50 printf("Game Over.\n"); 51 } 52 return 0; 53 }
View Code