[NOI2018]归程
今年D1T1,平心而论,如果能想到kruskal重构树还是很简单的。
......苟屁啊!虽然跟其他的比是简单些,但是思维难度中上,代码难度中上,怎么看都很符合NOI T1啊。
本题还有可持久化并查集的做法,以高度为版本。我没有打......
言归正传,来看题。
给你一个无向图,每条边有高度和长度。每次询问,从点s出发,只能经过高度大于h的边所能到达的点中,距1号点最近的点的距离。强制在线。
n<=200000,m<=400000,q<=400000
首先,离线有65分,十分可观。我们把边和询问都按照高度排序。然后依次加入,并查集维护连通块内dis最小值。
克鲁斯卡尔重构树解法:
首先讲什么是克鲁斯卡尔重构树:说起来也蛮简单,就是你把边排序,加边的时候如果连通就不加,否则新建节点代表这个连通块,边的两端所在连通块的代表节点作为这个新节点的两个子节点。
这样你要查询高度h时dis min,只需维护一个节点所代表连通块内dis最小值即可。每次向上跳,可以发现一条链上的dis min单调不增。然后就倍增了,类似lca。
啊我到底在口胡什么。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <queue> 5 6 const int N = 200010, INF = 0x7f7f7f7f; 7 8 struct Edge_1 { 9 int v, len, nex; 10 }edge_1[N << 2]; int top_1; // for dijkstra 11 12 struct POI { 13 int id, dis; 14 inline bool operator <(const POI &d) const { 15 return dis > d.dis; 16 } 17 POI(int a, int b) { 18 id = a; 19 dis = b; 20 } 21 }; // for dijkstra 22 23 struct Edge_2 { 24 int u, v, h; 25 inline bool operator <(const Edge_2 &d) const { 26 return h > d.h; 27 } 28 }edge_2[N << 1]; int top_2; // for sort kruskal 29 30 struct UFS { 31 int fa[N * 3]; 32 inline void clear() { 33 for(int i = 1; i < N * 3; i++) { 34 fa[i] = i; 35 } 36 return; 37 } 38 UFS() { 39 clear(); 40 } 41 int find(int x) { 42 if(fa[x] == x) { 43 return x; 44 } 45 return fa[x] = find(fa[x]); 46 } 47 inline void merge(int x, int y) { // x <- y 48 fa[find(y)] = find(x); 49 return; 50 } 51 }ufs; 52 53 int e[N], dis[N * 3]; // for dijkstra 54 int fa[N * 3][21], tot, h[N * 3]; // for kruskal 55 56 inline void add_1(int x, int y, int z) { 57 top_1++; 58 edge_1[top_1].v = y; 59 edge_1[top_1].len = z; 60 edge_1[top_1].nex = e[x]; 61 e[x] = top_1; 62 return; 63 } 64 65 inline void add_2(int x, int y, int z) { 66 top_2++; 67 edge_2[top_2].u = x; 68 edge_2[top_2].v = y; 69 edge_2[top_2].h = z; 70 return; 71 } 72 73 inline void clear() { 74 top_1 = 0; 75 top_2 = 0; 76 memset(e, 0, sizeof(e)); 77 ufs.clear(); 78 return; 79 } 80 81 inline void dijkstra() { 82 std::priority_queue<POI> Q; 83 memset(dis, 0x3f, sizeof(dis)); 84 dis[1] = 0; 85 Q.push(POI(1, 0)); // POI(id, dis) 86 while(!Q.empty()) { 87 while(!Q.empty() && dis[Q.top().id] != Q.top().dis) { 88 Q.pop(); 89 } 90 if(Q.empty()) { 91 break; 92 } 93 int x = Q.top().id; 94 Q.pop(); 95 for(int i = e[x]; i; i = edge_1[i].nex) { 96 int y = edge_1[i].v; 97 if(dis[y] > dis[x] + edge_1[i].len) { 98 dis[y] = dis[x] + edge_1[i].len; 99 Q.push(POI(y, dis[y])); 100 } 101 } 102 } 103 return; 104 } 105 106 inline void add(int p) { 107 int x = ufs.find(edge_2[p].u); 108 int y = ufs.find(edge_2[p].v); 109 if(x == y) { 110 return; 111 } 112 ++tot; 113 fa[x][0] = tot; 114 fa[y][0] = tot; 115 ufs.merge(tot, x); 116 ufs.merge(tot, y); 117 h[tot] = edge_2[p].h; 118 dis[tot] = std::min(dis[x], dis[y]); 119 return; 120 } 121 122 inline int solve(int x, int high) { 123 int t = 20; 124 while(t >= 0) { 125 if(h[fa[x][t]] > high) { 126 x = fa[x][t]; 127 } 128 t--; 129 } 130 return dis[x]; 131 } 132 133 int main() { 134 int T; 135 scanf("%d", &T); 136 while(T--) { 137 int n, m; 138 scanf("%d%d", &n, &m); 139 tot = n; 140 h[0] = -1; 141 for(int i = 1, x, y, z, w; i <= m; i++) { 142 scanf("%d%d%d%d", &x, &y, &z, &w); 143 add_1(x, y, z); 144 add_1(y, x, z); 145 add_2(x, y, w); 146 } 147 148 // prework 149 dijkstra(); 150 std::sort(edge_2 + 1, edge_2 + m + 1); 151 for(int i = 1; i <= m; i++) { 152 add(i); 153 } 154 for(int i = 1; i <= 20; i++) { 155 for(int x = 1; x <= tot; x++) { 156 fa[x][i] = fa[fa[x][i - 1]][i - 1]; 157 } 158 } 159 160 int q, k, s, op, high, la = 0; 161 scanf("%d%d%d", &q, &k, &s); 162 while(q--) { 163 scanf("%d%d", &op, &high); 164 op = (op + k * la - 1) % n + 1; 165 high = (high + k * la) % (s + 1); 166 la = solve(op, high); 167 printf("%d\n", la); 168 } 169 clear(); 170 } 171 return 0; 172 }
170行代码还行。