1. 程式人生 > >hdu5441(並查集+離線處理)

hdu5441(並查集+離線處理)

這樣的 而是 遍歷 城市 有一種 ron for else num

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5441

題意:

  根據輸入的三個整數n、m、q,表示有n座城市,m條路,q次詢問。下面給出m行,每行三個數start、end、w,分別是這條道路的起點城市、終點城市、“權值”。然後給出q次詢問的數值,每次詢問的結果是符合條件的路有多少條。條件就是:如果這條路的權值比給的限制值要小,那麽這條路就是可行的。註意就是:對於一條路的添加來說,只要a到b之間有路,那麽符合條件的路就會有兩條,一條是a到b,還有一條是b到a。比如:添加了兩條路,a到b、b到c,那麽其實答案就是6條,(a,b)、(a,c)、(b,a)、(b,c)、(c,a)、(c,b)。

思路:

  剛開始用常規的並查集做法來寫,發現會TLE,看了下數據,我之前那種遍歷方法絕對會超時啊……看到q次詢問就不加思索的對每次詢問進行遍歷,這樣每次詢問就要遍歷一遍那m組數據,雖然實際可能不用遍歷完m組,但是這個時間復雜度是很高的。然後網上學習了下,發現有一種方法很好,那就是不光對load[i].w進行排序,還可以對這q次詢問的限制值進行排序這樣的話只需要對m組數據遍歷一次就夠了。貌似這種方法有個名稱叫做“離線處理”?(我個人理解應該是把查詢的值先讀入,讀入後不在線處理,而是先存起來,排序之後再在遍歷m組數據的時候使用)因為輸出的時候是要按照輸入時的順序對應輸出,所以這裏需要有個表示查詢值編號的數據。然後計算公式的話,兩個集合合並,有增量有減量,合並後原來那兩個集合不存在了,減量就是n1*(n1-1)、n2*(n2-1);合並後新出現的新集合存在一個增量:(n1+n2)*(n1+n2-1)。

代碼:

  1 #include<iostream>
  2 #include<algorithm>
  3 using namespace std;
  4 
  5 const int maxn = 2e4 + 10;
  6 const int max_edge = 1e5 + 10;
  7 const int max_q = 5e3 + 10;
  8 
  9 int fa[maxn];
 10 int num[maxn];    //記錄這個集合中點的個數
 11 
 12 struct Load
 13 {
 14     int start;
 15     int
end; 16 int w; 17 } load[max_edge]; 18 19 struct Qnode 20 { 21 int sum; 22 int id; //記錄編號,便於最後輸出時是按編號輸出 23 } qnode[max_q]; 24 25 bool cmp1(Load a, Load b) //按權值從小到大排序 26 { 27 return a.w < b.w; 28 } 29 30 bool cmp2(Qnode a, Qnode b) //按限制值從小到大排序 31 { 32 return a.sum < b.sum; 33 } 34 35 void init(int n) 36 { 37 for(int i = 1; i <= n; i++) 38 { 39 fa[i] = i; 40 num[i] = 1; 41 } 42 return; 43 } 44 45 int find(int x) 46 { 47 if(fa[x] == x) 48 { 49 return x; 50 } 51 else 52 { 53 return fa[x] = find(fa[x]); 54 } 55 } 56 57 int main() 58 { 59 ios::sync_with_stdio(false); 60 int t; 61 int n, m, q; 62 63 long long ans[max_q]; 64 65 cin >> t; 66 67 while(t--) 68 { 69 cin >> n >> m >> q; 70 init(n); 71 for(int i = 1; i <= m; i++) 72 { 73 cin >> load[i].start >> load[i].end >> load[i].w; 74 //發現這個交換是多余的…… 75 /*if(load[i].start > load[i].end) 76 { 77 swap(load[i].start, load[i].end); 78 }*/ 79 } 80 sort(load + 1, load + 1 + m, cmp1); 81 82 for(int i = 1; i <= q; i++) 83 { 84 cin >> qnode[i].sum; 85 qnode[i].id = i; //編號按順序賦值 86 } 87 sort(qnode + 1, qnode + 1 + q, cmp2); 88 89 int cnt = 1; 90 long long tmp = 0; 91 for(int i = 1; i <= m; i++) //這裏是直接遍歷路徑數據,效率比我之前想的高多了…… 92 { 93 int x = find(load[i].start); 94 int y = find(load[i].end); 95 96 while(load[i].w > qnode[cnt].sum && cnt <= q) 97 { 98 ans[qnode[cnt].id] = tmp; //沒有新的路添加,所以答案還是tmp,暫時不變 99 cnt++; //當前這個小的qnode不能滿足大於這條路的權值,那麽就繼續往下看比當前大的qnode是否符合條件 100 } 101 if(x != y) 102 { 103 long long n1 = num[x], n2 = num[y]; 104 tmp += (n1 + n2) * (n1 + n2 - 1); 105 tmp -= (n1 * (n1 - 1) + n2 * (n2 - 1)); 106 fa[x] = y; 107 num[y] += num[x]; //x合並到y上,則x上點的個數也要加到y上 108 } 109 } 110 while(cnt <= q) //這裏的意思是,路徑數據已經全部遍歷完了,可能詢問還沒有結束,較小的詢問已經處理過全部數據,那麽較大的也一定能 111 { 112 ans[qnode[cnt++].id] = tmp; 113 } 114 for(int i = 1; i <= q; i++) 115 { 116 cout << ans[i] << endl; 117 } 118 } 119 return 0; 120 }

hdu5441(並查集+離線處理)