[SCOI2013]摩托车交易 kruskal重构树(最大生成树) 倍增
题解:
这题想法简单,,,写起来真的是失智,找了几个小时的错误结果是inf没开到LL范围。。。。
首先我们需要找到任意两点之间能够携带黄金的上限值,因为是在经过的道路权值中取min,我们要使得这个min值最大,就应该要在最大生成树上寻找正确的边。求出最大生成树后我们需要在上面倍增寻找权值最小的边,这条边的权值即为携带黄金的上限值。
于是你可以写最大生成树也可以写kruskal重构树,这里我写的是kruskal重构树,这样以来,因为kruskal重构树的性质,我们只需要寻找对应2个节点的lca,这个lca的点权即为我们要找的值。
但是注意到题中有一些点可以被列车连通,因为在这些被联通的点之间移动不会带来任何限制,因此我们可以把这些有列车的节点看做一个点(缩点)
然后注意到题目要求的仅仅是每个卖黄金的地方卖出的黄金数,而且在任意地方买卖的黄金并没有任何其他限制(如价格之类的),因此我们可以每到一个地方就买光所有黄金,然后如果带不到下一个地方去,我们就当我们之前没买过,对道路的上限取min即可。如果最后黄金有剩余,我们也可以直接当做我们没买过。
于是这题就做完了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 501000 5 #define ac 1001000 6 #define LL long long 7 #define inf 1000000000000000LL//!!!!!!!!!!!!!!!!!!!!! 8 /*因为只需要关心卖出了多少,所以遇到买入的就能买就买,如果要丢弃就当我没买过, 9 如果有剩余也当我没买过,然后有列车的点可以相互到达,所以就缩点缩起来,然后有路上有负载上限, 10 所以就跑最大生成树(重构树),然后倍增查最大上限是多少,把剩余黄金对上限取min即可。*/ 11 12 int n, m, q, cnt, who; 13 LL have; 14 int Head[ac], date[ac], Next[ac], tot; 15 int father[AC], vis[AC], belong[AC], dep[ac]; 16 LL f[ac][21], power[ac];//点权or边权(叶节点就是点权,不然就是边权) 17 18 struct road{ 19 int x, y;LL dis; 20 }way[ac]; 21 22 inline bool cmp(road a, road b){ 23 return a.dis > b.dis; 24 } 25 26 inline int read() 27 { 28 int x = 0;char c = getchar(); bool z = false; 29 while(c > '9' || c < '0') { 30 if(c == '-') z = true; 31 c = getchar(); 32 } 33 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 34 if(!z) return x; 35 else return -x; 36 } 37 38 inline int find(int x){ 39 if(father[x] == x) return x; 40 else return father[x] = find(father[x]); 41 } 42 43 inline void add(int f, int w){ 44 date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, father[w] = f; 45 //printf("%d ---> %d : %d\n", f, w, power[cnt]); 46 } 47 48 inline void upmin(LL &a, LL b){ 49 if(b < a) a = b; 50 } 51 52 void kruskal()//重构树 53 { 54 int b = 2 * n; 55 for(R i = 1; i <= b; i ++) father[i] = i; 56 for(R i = 1; i <= m; i ++) 57 { 58 int fx, fy; 59 fx = find(belong[way[i].x]), fy = find(belong[way[i].y]); 60 if(fx == fy) continue; 61 power[++cnt] = way[i].dis; 62 //printf("%d %d %d\n", way[i].x, way[i].y, way[i].dis); 63 add(cnt, fx), add(cnt, fy); 64 } 65 power[cnt + 1] = inf, dep[cnt] = 1, f[cnt][0] = cnt; 66 } 67 68 void dfs(int x)//倍增 69 { 70 int now; 71 // printf("!!!%d\n", power[x]); 72 for(R i = 1; i <= 20; i ++) 73 f[x][i] = f[f[x][i - 1]][i - 1]; 74 for(R i = Head[x]; i; i = Next[i]) 75 now = date[i], f[now][0] = x, dep[now] = dep[x] + 1, dfs(now); 76 } 77 78 int lca(int x, int y)//要先倍增找到最小上限 79 { 80 if(dep[x] < dep[y]) swap(x, y); 81 for(R i = 20; i >= 0; i --) 82 if(dep[f[x][i]] >= dep[y]) x = f[x][i]; 83 for(R i = 20; i >= 0; i --) 84 if(f[x][i] != f[y][i]) 85 x = f[x][i], y = f[y][i]; 86 if(x != y) return power[f[x][0]]; 87 else return power[x]; 88 } 89 90 void go(int f, int w) 91 { 92 LL lim = (belong[f] == belong[w]) ? inf : lca(belong[f], belong[w]); 93 /*if(find(belong[f]) != find(belong[w])) 94 { 95 for(R i = w; i <= n; i ++) printf("0\n"); 96 exit(0); 97 }*/ 98 upmin(have, lim); 99 if(power[w] > 0) have += power[w]; 100 else 101 { 102 if(have > - power[w]) 103 have += power[w], printf("%lld\n", -power[w]); 104 else printf("%lld\n", have), have = 0; 105 } 106 } 107 108 void pre() 109 { 110 n = cnt = read(), m = read(), q = read(); 111 for(R i = 1; i <= n; i ++) vis[i] = read(), belong[i] = i;//读入每个城市的访问顺序 112 for(R i = 1; i <= n; i ++) power[i] = read();//读入每个城市的订单 113 for(R i = 1; i <= m; i ++)//读入边 114 way[i].x = read(), way[i].y = read(), way[i].dis = read(); 115 for(R i = 1; i <= q; i ++)//读入有列车的城市 116 { 117 int a = read(); 118 if(!who) who = a; 119 belong[a] = who; 120 } 121 sort(way + 1, way + m + 1, cmp); 122 } 123 124 void work() 125 { 126 if(power[vis[1]] > 0) have = power[vis[1]]; 127 else printf("0\n"); 128 for(R i = 1; i < n; i ++) go(vis[i], vis[i + 1]); 129 } 130 131 int main() 132 { 133 // freopen("in.in", "r", stdin); 134 pre(); 135 kruskal(); 136 dfs(cnt); 137 work(); 138 // fclose(stdin); 139 return 0; 140 }