【BZOJ1050】[HAOI2006]旅行comf
Description
给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000)。给你两个顶点S和T
,求一条路径,使得路径上最大边和最小边的比值最小。如果S和T之间没有路径,输出”IMPOSSIBLE”,否则输出
这个比值,如果需要,表示成一个既约分数。 备注: 两个顶点之间可能有多条路径。
Input
第一行包含两个正整数,N和M。下来的M行每行包含三个正整数:x,y和v。表示景点x到景点y之间有一条双向
公路,车辆必须以速度v在该公路上行驶。最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速
度比最小的路径。s和t不可能相同。
1<N<=500,1<=x,y<=N,0<v<30000,0<M<=5000
Output
如果景点s到景点t没有路径,输出“IMPOSSIBLE”。否则输出一个数,表示最小的速度比。如果需要,输出一
个既约分数。
Sample Input
【样例输入1】
4 2
1 2 1
3 4 2
1 4
【样例输入2】
3 3
1 2 10
1 2 5
2 3 8
1 3
【样例输入3】
3 2
1 2 2
2 3 4
1 3
4 2
1 2 1
3 4 2
1 4
【样例输入2】
3 3
1 2 10
1 2 5
2 3 8
1 3
【样例输入3】
3 2
1 2 2
2 3 4
1 3
Sample Output
【样例输出1】
IMPOSSIBLE
【样例输出2】
5/4
【样例输出3】
2
IMPOSSIBLE
【样例输出2】
5/4
【样例输出3】
2
Solution
两种做法:
1. O(m^2)的暴力
我们发现一个性质:答案一定是排序后一个后缀的最小生成树。所以暴力就是直接枚举每个后缀跑最小生成树。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 6 #ifdef WIN32 7 #define LL "%I64d" 8 #else 9 #define LL "%lld" 10 #endif 11 12 #ifdef CT 13 #define debug(...) printf(__VA_ARGS__) 14 #define setfile() 15 #else 16 #define debug(...) 17 #define filename "" 18 #define setfile() freopen(filename".in", "r", stdin); freopen(filename".out", "w", stdout) 19 #endif 20 21 #define R register 22 #define getc() (S == T && (T = (S = B) + fread(B, 1, 1 << 15, stdin), S == T) ? EOF : *S++) 23 #define dmax(_a, _b) ((_a) > (_b) ? (_a) : (_b)) 24 #define dmin(_a, _b) ((_a) < (_b) ? (_a) : (_b)) 25 #define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0) 26 #define cmin(_a, _b) (_a > (_b) ? _a = (_b) : 0) 27 #define cabs(_x) ((_x) < 0 ? (- (_x)) : (_x)) 28 char B[1 << 15], *S = B, *T = B; 29 inline int F() 30 { 31 R char ch; R int cnt = 0; R bool minus = 0; 32 while (ch = getc(), (ch < '0' || ch > '9') && ch != '-') ; 33 ch == '-' ? minus = 1 : cnt = ch - '0'; 34 while (ch = getc(), ch >= '0' && ch <= '9') cnt = cnt * 10 + ch - '0'; 35 return minus ? -cnt : cnt; 36 } 37 #define maxn 510 38 #define maxm 5010 39 struct Edge 40 { 41 int a, b, w; 42 inline bool operator < (const Edge &that) const {return w < that.w;} 43 }e[maxm]; 44 int Fa[maxn], n, m; 45 inline void init() 46 { 47 for (R int i = 1; i <= n; ++i) Fa[i] = i; 48 } 49 int gcd(R int a, R int b) {return !b ? a : gcd(b, a % b);} 50 int Find(R int x) {return Fa[x] == x ? x : Fa[x] = Find(Fa[x]);} 51 int main() 52 { 53 // setfile(); 54 n = F(), m = F(); 55 for (R int i = 1; i <= m; ++i) 56 e[i] = (Edge) {F(), F(), F()}; 57 R int s = F(), t = F(); 58 std::sort(e + 1, e + m + 1); 59 R int ans1 = 1, ans2 = 0; 60 for (R int i = 1; i <= m; ++i) 61 { 62 init(); 63 R int up; 64 R bool flag = 1; 65 for (R int j = i; j <= m && flag; ++j) 66 { 67 R int f1 = Find(e[j].a), f2 = Find(e[j].b); 68 if (f1 != f2) Fa[f1] = f2; 69 if (Find(s) == Find(t)) flag = 0, up = e[j].w; 70 } 71 if (i == 1 && flag) return puts("IMPOSSIBLE"), 0; 72 if (!flag && (double) up / e[i].w < (double) ans1 / (double) ans2) 73 { 74 ans1 = up; 75 ans2 = e[i].w; 76 } 77 } 78 if (ans1 % ans2 == 0) printf("%d\n", ans1 / ans2 ); 79 else printf("%d/%d\n", ans1 / gcd(ans1, ans2), ans2 / gcd(ans1, ans2) ); 80 return 0; 81 }
2.O(mlogm)
既然不能暴力做最小生成树,那就用LCT维护最小生成树即可。
1 #include <cstdio> 2 #include <algorithm> 3 4 #define R register 5 #define inf 0x7fffffff 6 #define maxn 100010 7 #define maxm 200010 8 struct ed{ 9 int a, b, w; 10 inline bool operator < (const ed &that) const {return w < that.w;} 11 }e[maxm]; 12 struct node *null; 13 struct node 14 { 15 node *ch[2], *fa, *pos; 16 int val, mn; 17 bool rev; 18 inline bool type() 19 { 20 return fa -> ch[1] == this; 21 } 22 inline bool check() 23 { 24 return fa -> ch[type()] == this; 25 } 26 inline void pushup() 27 { 28 pos = this; mn = val; 29 ch[0] -> mn < mn ? mn = ch[0] -> mn, pos = ch[0] -> pos : 0; 30 ch[1] -> mn < mn ? mn = ch[1] -> mn, pos = ch[1] -> pos : 0; 31 } 32 inline void pushdown() 33 { 34 if (rev) 35 { 36 ch[0] -> rev ^= 1; 37 ch[1] -> rev ^= 1; 38 std::swap(ch[0], ch[1]); 39 rev ^= 1; 40 } 41 } 42 inline void pushdownall() 43 { 44 if (check()) fa -> pushdownall(); 45 pushdown(); 46 } 47 inline void rotate() 48 { 49 R bool d = type(); R node *f = fa, *gf = f -> fa; 50 (fa = gf, f -> check()) ? fa -> ch[f -> type()] = this : 0; 51 (f -> ch[d] = ch[!d]) != null ? (ch[!d] -> fa = f) : 0; 52 (ch[!d] = f) -> fa = this; 53 f -> pushup(); 54 } 55 inline void splay(R bool npda = 1) 56 { 57 if (npda) pushdownall(); 58 for (; check(); rotate()) 59 if (fa -> check()) 60 { 61 if (type() == fa -> type()) fa -> rotate(); 62 else rotate(); 63 } 64 pushup(); 65 } 66 inline node *access() 67 { 68 R node *i = this, *j = null; 69 for ( ; i != null; i = (j = i) -> fa) 70 { 71 i -> splay(); 72 i -> ch[1] = j; 73 i -> pushup(); 74 } 75 return j; 76 } 77 inline void make_root() 78 { 79 access(); splay(); rev ^= 1; 80 } 81 inline void link(R node *that) 82 { 83 make_root(); 84 fa = that; 85 splay(0); 86 } 87 inline void cut(R node *that) 88 { 89 make_root(); 90 that -> access(); 91 that -> splay(0); 92 that -> ch[0] = fa = null; 93 that -> pushup(); 94 } 95 }mem[1000010], *ncnt = mem, *edge = mem + 100010; 96 int gcd(R int a, R int b) 97 { 98 return b ? gcd(b, a % b) : a; 99 } 100 inline node *query(node *a, node *b) 101 { 102 a -> make_root(); 103 b -> access(); b -> splay(0); 104 return b -> pos; 105 } 106 int Fa[maxn], m, n; 107 int Find(R int x) {return Fa[x] == x ? x : Fa[x] = Find(Fa[x]);} 108 int main() 109 { 110 null = mem; 111 *null = (node) {{null, null}, null, null, inf, inf, 0}; 112 scanf("%d%d", &n, &m); 113 for (R int i = 1; i <= m; ++i) scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].w); 114 R int s, t; scanf("%d%d", &s, &t); 115 std::sort(e + 1, e + m + 1); 116 R int up = 1, down = 0; 117 for (R int i = 1; i <= n; ++i) 118 { 119 Fa[i] = i; 120 mem[i] = (node) {{null, null}, null, null, inf, inf, 0}; 121 } 122 R int con = 0; 123 for (R int i = 1; i <= m; ++i) 124 { 125 R int mi, a = e[i].a, b = e[i].b; 126 if (a == b) continue; 127 R int f1 = Find(a), f2 = Find(b); 128 *(edge + i) = (node) {{null, null}, null, null, e[i].w, e[i].w, 0}; 129 if (f1 != f2) 130 { 131 Fa[f1] = f2; 132 (mem + a) -> link(edge + i); 133 (mem + b) -> link(edge + i); 134 ++con; 135 } 136 else 137 { 138 R node *p = query(mem + a, mem + b); 139 (mem + e[p - edge].a) -> cut(p); 140 (mem + e[p - edge].b) -> cut(p); 141 (mem + a) -> link(edge + i); 142 (mem + b) -> link(edge + i); 143 } 144 if (Find(s) == Find(t)) 145 { 146 mi = query(mem + s, mem + t) -> val; 147 // 从s到t的链上最小值。 148 if (1ll * up * mi > 1ll * e[i].w * down) 149 { 150 up = e[i].w; 151 down = mi; 152 } 153 } 154 } 155 if (down == 0) puts("IMPOSSIBLE"); 156 else if (up % down == 0) printf("%d\n", up / down); 157 else printf("%d/%d\n", up / gcd(up, down), down / gcd(up, down)); 158 return 0; 159 }