題目4 : 觀光旅行
描述
小Hi去H市旅遊,H 市有 n 個旅遊景點,有 m 條雙向道路連接這些旅遊景點,使得任意兩個景點之間都至少有一條路徑可以到達。每條道路都會有一個不同的正整數 w 描述這條道路的擁擠係數。小Hi非常討厭擁擠的感覺,因此當小Hi嘗試從景點 u 去到景點 v 的時候,總會儘可能地選擇一條路徑,使得這條路徑中最大的擁擠係數是所有 u 到 v 的路徑中最小的。小Hi會對這條路徑中最擁擠的道路印象深刻,並且認爲它是景點 u 和景點 v 的關鍵道路。
現在小Hi想要知道,對於H市中的每一條道路,是否存在兩個景點 u 和 v,使得它是 u 和 v 的關鍵道路。
輸入
第一行輸入兩個正整數 n 和 m,分別表示 H 市的景點數量和道路數量。
接下來 m 行,第 i 行輸入三個正整數 ui, vi 和 wi,表示有一條連接 ui 和 vi 的雙向道路,它的擁擠係數爲 wi,保證沒有兩條道路的擁擠係數是相同的。
2 ≤ n ≤ 105, n-1 ≤ m ≤ 2 × 105, 1 ≤ ui, vi ≤ n, 1 ≤ wi ≤ 109, wi ≠ wj(i ≠ j)
輸出
輸出共 m 行,第 i 行輸出用一個空格隔開的兩個整數 x 和 y,滿足 x < y 且輸入的第 i 條邊是 x 和 y 的關鍵道路。如果不存在滿足要求的 x 和 y,則輸出“0 0”(不含引號)。如果有多個滿足條件的 x 和 y,輸出其中 x 最大的,如果還有多個滿足條件的,輸出其中 y 最小的。
6 7 1 2 6 1 3 2 2 3 3 2 4 1 3 6 9 3 5 8 4 6 4樣例輸出
0 0 1 3 3 4 2 4 0 0 5 6 4 6
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 2e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
struct Edge
{
int x, y, z, o;
bool operator < (const Edge & b)const
{
return z < b.z;
}
}edge[N];
int f[N];
int sz[N];
set<int>sot[N];
int find(int x)
{
return f[x] == x ? x : f[x] = find(f[x]);
}
pair<int, int>ans[N];
void update(int o, int x, int y)
{
if (!sot[x].size())return;
if (!sot[y].size())return;
int bigx = *--sot[x].end();
set<int>::iterator it = sot[y].lower_bound(bigx);
if (it == sot[y].begin())return;
int py = *--it;
int px = *sot[x].lower_bound(py);
swap(px, py);
if (px > ans[o].first || px == ans[o].first && py < ans[o].second)
{
ans[o] = { px,py };
}
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
for (int i = 1; i <= n; ++i)
{
f[i] = i;
sz[i] = 1;
sot[i].clear();
sot[i].insert(i);
}
for (int i = 1; i <= m; ++i)
{
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
edge[i] = { x,y,z,i };
ans[i] = { 0, 0 };
}
sort(edge + 1, edge + m + 1);
for (int i = 1; i <= m; ++i)
{
int x = edge[i].x;
int y = edge[i].y;
int fx = find(x);
int fy = find(y);
if (fx == fy)continue;
//fy -> fx
if (sz[fx] < sz[fy])
{
swap(fx, fy);
swap(x, y);
}
update(edge[i].o, fx, fy);
update(edge[i].o, fy, fx);
//合併操作
for (auto it : sot[fy])sot[fx].insert(it);
sot[fy].clear();
f[fy] = fx;
sz[fx] += sz[fy];
}
for (int i = 1; i <= m; ++i)
{
printf("%d %d\n", ans[i].first, ans[i].second);
}
}
return 0;
}
/*
【題意】
http://hihocoder.com/contest/offers23/problem/4
小Hi去H市旅遊,H 市有 n 個旅遊景點,有 m 條雙向道路連接這些旅遊景點,使得任意兩個景點之間都至少有一條路徑可以到達。
每條道路都會有一個不同的正整數 w 描述這條道路的擁擠係數。小Hi非常討厭擁擠的感覺,因此當小Hi嘗試從景點 u 去到景點 v 的時候,總會儘可能地選擇一條路徑,使得這條路徑中最大的擁擠係數是所有 u 到 v 的路徑中最小的。
小Hi會對這條路徑中最擁擠的道路印象深刻,並且認爲它是景點 u 和景點 v 的關鍵道路。
現在小Hi想要知道,對於H市中的每一條道路,是否存在兩個景點 u 和 v,使得它是 u 和 v 的關鍵道路。
如果有多個滿足條件的 x 和 y,輸出其中 x 最大的,如果還有多個滿足條件的,輸出其中 y 最小的。
【分析】
顯然,如果我們考慮從任意兩點間的最小擁擠係數的街道,我們會考慮從小到大的順序逐漸加邊。
這樣子,連通性會形成一棵樹,而且是最小生成樹。
而任意兩點的聯通所需要的成本,則是這兩個點連通性達成時的最後一條邊。
也就是說,只有最小生成樹的樹邊可能是關鍵道路。
而我們需要找到樹邊兩側的{x, y},使得x <= y,且在x儘可能大的條件下,y儘可能小。
首先,當一條邊確定爲樹邊的時候,這條邊的影響,是合併了2個連通塊,這裏可以通過set的啓發式合併維持複雜度。
然後,考慮{x,y}的選擇。顯然第一關鍵字是x,於是——
1,先在集合A中找到最大的y_,使得對x的限制儘可能小。
2,再在集合B中找到比y_剛好小的最大的x,這是確定的第一關鍵字
3,然後在集合A中找到比x剛好大的數y,這就是確定的第二關鍵字。
【時間複雜度&&優化】
O(nlognlogn)
*/