[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
", 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
", 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
", 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
");
 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
", -power[w]);
104         else printf("%lld
", 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
");
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 }
View Code
原文地址:https://www.cnblogs.com/ww3113306/p/9806422.html