CodeForces 76A Gift - 最小生成树
The kingdom of Olympia consists of N cities and M bidirectional roads. Each road connects exactly two cities and two cities can be connected with more than one road. Also it possible that some roads connect city with itself making a loop.
All roads are constantly plundered with bandits. After a while bandits became bored of wasting time in road robberies, so they suggested the king of Olympia to pay off. According to the offer, bandits want to get a gift consisted of gold and silver coins. Offer also contains a list of restrictions: for each road it is known gi — the smallest amount of gold and si — the smallest amount of silver coins that should be in the gift to stop robberies on the road. That is, if the gift contains agold and b silver coins, then bandits will stop robberies on all the roads that gi ≤ a and si ≤ b.
Unfortunately kingdom treasury doesn't contain neither gold nor silver coins, but there are Olympian tugriks in it. The cost of one gold coin in tugriks is G, and the cost of one silver coin in tugriks is S. King really wants to send bandits such gift that for any two cities there will exist a safe path between them. Your task is to find the minimal cost in Olympian tugriks of the required gift.
Input
The first line of the input contains two integers N and M (2 ≤ N ≤ 200, 1 ≤ M ≤ 50 000) — the number of cities and the number of roads, respectively. The second line contains two integers G and S (1 ≤ G, S ≤ 109) — the prices of gold and silver coins in tugriks. The following M lines contain information about the offer. Each of the records in list is given as four integers xi, yi, gi, si, where xi and yi are the numbers of cities that the road connects and gi, si are minimal gold and silver coins requirements for the i-th road (1 ≤ xi, yi ≤ N, 1 ≤ gi, si ≤ 109). Cities are numbered from 1 to N. It is possible that there are more than one road between a pair of cities. It is possible that a road connects the city with itself.
Output
The output should contain the minimal cost of the gift in Olympian tugriks. If there is no gift that satisfies the given requirements output .
Example
3 3
2 1
1 2 10 15
1 2 4 20
1 3 5 1
30
题目大意是说每条边,带两条权值g,s,给定G和S,设a为所有使用的边中g的最大值,b为所有使用的边中s的最大值,求连通n个城市的最小的(a * G + b * S)
首先思考暴力吧,按照第一种权值给边排序,然后Kruskal乱搞,总时间复杂度$O\left(m^{2}\log m\right)$,高兴地发现这么会严重TLE。
继续思考,二分第一种权值,然后Kruskal顶上,总时间复杂度$O\left(m\cdot \log^{2}m\right)$,看起来可以过,然而我可以举出不满足二分条件的反例。假如G值特别小,S值特别大,边数远远大于点数,一条边g值比较大,但是s值非常的小,二分的话很可能会忽略这一条边,导致答案是错的。
继续思考人生。继续第一种思路,先按照第一种权值进行排序,考虑上一次的最小生成树,每次只会添加一条边,因此有两种方法可以在O(n)的时间内处理完。
- 在一棵树上添加一条边,会形成一个环,找到这个环,删去其中s值最大的一条边(由于插入的边不能被删去,这个的过程就是树上两点间的简单路径找s值最大的一条边,这个过程可以用LCT做到单次操作$O\left(\log n\right)$)。因为上一个生成树最有n个点,n - 1条边,所以时间复杂度为O(n)
- 用一个数组维护,然后插入排序,接着做一次最小生成树。时间复杂度仍然是O(n)
每次生成完一棵树就更新答案就行了。
贡献一堆WA,ans的初值不能设小了,至少(1 << 61)吧!
Code
1 /** 2 * CodeForces 3 * Problem#76A 4 * Accepted 5 * Time:248ms 6 * Memory:2820k 7 */ 8 #include<iostream> 9 #include<fstream> 10 #include<sstream> 11 #include<algorithm> 12 #include<cstdio> 13 #include<cstring> 14 #include<cstdlib> 15 #include<cctype> 16 #include<cmath> 17 #include<ctime> 18 #include<map> 19 #include<stack> 20 #include<set> 21 #include<queue> 22 #include<vector> 23 #ifndef WIN32 24 #define AUTO "%lld" 25 #else 26 #define AUTO "%I64d" 27 #endif 28 using namespace std; 29 typedef bool boolean; 30 #define inf 0xfffffff 31 #define smin(a, b) (a) = min((a), (b)) 32 #define smax(a, b) (a) = max((a), (b)) 33 template<typename T> 34 inline boolean readInteger(T& u) { 35 char x; 36 int aFlag = 1; 37 while(!isdigit((x = getchar())) && x != '-' && x != -1); 38 if(x == -1) { 39 ungetc(x, stdin); 40 return false; 41 } 42 if(x == '-') { 43 aFlag = -1; 44 x = getchar(); 45 } 46 for(u = x - '0'; isdigit((x = getchar())); u = u * 10 + x - '0'); 47 u *= aFlag; 48 ungetc(x, stdin); 49 return true; 50 } 51 52 typedef class union_found{ 53 public: 54 int *f; 55 int points; 56 union_found():f(NULL), points(0) {} 57 union_found(int points):points(points) { 58 f = new int[(const int)(points + 1)]; 59 } 60 int find(int x) { 61 if(f[x] != x) return f[x] = find(f[x]); 62 return f[x]; 63 } 64 void unit(int fa, int so) { 65 int ffa = find(fa); 66 int fso = find(so); 67 f[fso] = ffa; 68 } 69 boolean connected(int a, int b) { 70 return find(a) == find(b); 71 } 72 void clear() { 73 for(int i = 0; i <= points; i++) 74 f[i] = i; 75 } 76 }union_found; 77 78 typedef class Edge { 79 public: 80 int from; 81 int end; 82 int g; 83 int s; 84 Edge(int from = 0, int end = 0, int g = 0, int s = 0):from(from), end(end), g(g), s(s) { } 85 }Edge; 86 87 int n, m; 88 long long G, S; 89 Edge* edges; 90 91 inline void init() { 92 readInteger(n); 93 readInteger(m); 94 readInteger(G); 95 readInteger(S); 96 edges = new Edge[(const int)(m + 1)]; 97 for(int i = 1; i <= m; i++) { 98 readInteger(edges[i].from); 99 readInteger(edges[i].end); 100 readInteger(edges[i].g); 101 readInteger(edges[i].s); 102 } 103 } 104 105 boolean cmp(const Edge& a, const Edge& b) { 106 if(a.g != b.g) return a.g < b.g; 107 return a.s < b.s; 108 } 109 110 int top = 0; 111 Edge *x; 112 union_found uf; 113 long long g0 = 0, s0 = 0; 114 long long res = (1LL << 61); 115 inline void solve() { 116 x = new Edge[(const int)(n + 5)]; 117 sort(edges + 1, edges + m + 1, cmp); 118 uf = union_found(n); 119 for(int i = 1; i <= m; i++) { 120 uf.clear(); 121 x[++top] = edges[i]; 122 for(int j = top; j > 1; j--) 123 if(x[j].s < x[j - 1].s) 124 swap(x[j], x[j - 1]); 125 g0 = 0, s0 = 0; 126 int fin = 0; 127 for(int j = 1; j <= top; j++) { 128 if(!uf.connected(x[j].from, x[j].end)) { 129 x[++fin] = x[j]; 130 smax(g0, (long long)x[j].g); 131 smax(s0, (long long)x[j].s); 132 uf.unit(x[j].from, x[j].end); 133 if(fin == n - 1) break; 134 } 135 } 136 if(fin == n - 1) 137 smin(res, g0 * G + s0 * S); 138 top = fin; 139 } 140 if(res == (1LL << 61)) 141 printf("-1\n"); 142 else printf(AUTO"\n", res); 143 } 144 145 int main() { 146 init(); 147 solve(); 148 return 0; 149 }