胡闹 一路畅通

题目大意

给你一个$n\le10^5$个点,$m\le2\times10^5$的无向图,每条边有一个权值$a_i<2^{31}$。

求一条从$S$点走到$T$的路径,这条路径上的边权最大值除以边权最小值应该全局最小,输出这个值。

解析

考虑先枚举一个上界,然后我们要让联通的下界尽量大。

怎么枚举上界呢?可以考虑把所有的边都按边权从大到小排序,然后类似$Kruskal$的方法来做。

如果新加入一条边,这条边两边是不联通的,那它显然是要保留的。

然后如果这条边两边是联通的,那么我们就应该把这个联通块中边权最小的边删掉,换成着一条边。

可以发现,这个联通块一定是一棵树。

现在我们要做的事情变成了:

  1. 维护一个森林
  2. 加边
  3. 删边
  4. 查询最小值

这个用一个$LCT$就可以维护好了。

答案怎么计算?非常简单,处理每一条边的时候都康康看看$S,T$是否联通,联通的话就就查询一下最小值就行了,这个直接用$Kruskal$里面的并查集就可以维护了。

注意一点,边权非常大,$inf$不能开太大!

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#define REP(i, s, e) for (int i = s; i <= e; i++)

#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 100000 + 10, maxm = 200000 + 10, maxN = maxn + maxm;

struct Edge
{
int x, y, z;
}E[maxN];
bool cmp(Edge A, Edge B) {return A.z > B.z;}

int n, m, S, T;
int f[maxN];
int find(int x) {return f[x] == x ? f[x] : f[x] = find(f[x]);}
void uni(int x, int y) {f[find(x)] = find(y);}

int fa[maxN], ch[maxN][2], s[maxN];
bool tag[maxN];
#define ls(p) ch[p][0]
#define rs(p) ch[p][1]
#define get(x) (rs(fa[x]) == x)
#define notroot(x) (ls(fa[x]) == x || rs(fa[x]) == x)

void pushup(int x)
{
s[x] = x;s[0] = 0;
if (E[s[x]].z < E[s[ls(x)]].z) s[x] = s[ls(x)];
if (E[s[x]].z < E[s[rs(x)]].z) s[x] = s[rs(x)];
}

void rotate(int x)
{
int y = fa[x], z = fa[y];
bool k = get(x);
if (ch[x][k ^ 1]) fa[ch[x][k ^ 1]] = y;
ch[y][k] = ch[x][k ^ 1];
if (notroot(y)) ch[z][get(y)] = x;
fa[fa[y] = x] = z;
ch[x][k ^ 1] = y;
pushup(y);pushup(x);
}
void pushdown(int x)
{
if (!tag[x]) return;
swap(ls(x), rs(x));
tag[ls(x)] ^= 1;tag[rs(x)] ^= 1;
tag[x] = 0;
}
void pushall(int x)
{
if (notroot(x)) pushall(fa[x]);
pushdown(x);
}
void splay(int x)
{
pushall(x);
while (notroot(x))
{
int y = fa[x];
if (notroot(y)) rotate(get(x) == get(y) ? y : x);
rotate(x);
}
}
void access(int x)
{
for (int t = 0; x; x = fa[t = x])
{
splay(x);
rs(x) = t;
pushup(x);
}
}
void makeroot(int x)
{
access(x);
splay(x);
tag[x] ^= 1;
}
void link(int x, int y)
{
makeroot(x);
fa[x] = y;
}
void cut(int x, int y)
{
makeroot(x);
access(y);
splay(y);
fa[x] = ls(y) = 0;
}
int query(int u, int v)
{
makeroot(u);
access(v);
splay(v);
return s[v];
}
long long ansu = 1 << 30, ansv = 1;

int main()
{
#ifndef ONLINE_JUDGE
freopen("B.in", "r", stdin);
freopen("B.out", "w", stdout);
#endif
cin >> n >> m;
REP(i, n + 1, n + m) scanf("%d%d%d", &E[i].x, &E[i].y, &E[i].z);
cin >> S >> T;
sort(E + n + 1, E + 1 + n + m, cmp);
REP(i, 1, n) f[i] = i;
REP(i, n + 1, n + m)
{
if (find(E[i].x) ^ find(E[i].y))
{
uni(E[i].x, E[i].y);
link(E[i].x, i);
link(E[i].y, i);
}
else
{
int pos = query(E[i].x, E[i].y);
if (E[pos].z <= E[i].z) continue;
cut(E[pos].x, pos);
cut(E[pos].y, pos);
link(E[i].x, i);
link(E[i].y, i);
}
if (find(S) == find(T))
{
int v = E[query(S, T)].z;
if (ansu * E[i].z >= ansv * v) ansu = v, ansv = E[i].z;
}
}
if (ansu % ansv == 0) printf("%lld\n", ansu / ansv);
else printf("%lld/%lld\n", ansu / __gcd(ansu, ansv), ansv / __gcd(ansu, ansv));
return 0;
}

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×