题目4 : 观光旅行
-
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
描述
小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 最小的。
#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)
*/