[NOI2018]归程 kruskal重构树
题解:
此题可以用可持久化并查集暴力水过,但正解是kruskal重构树。
不会kruskal重构树请戳:kruskal重构树
观察到车可以通过哪些边跟边的长度并没有关系,所以考虑用边的海拔排序建出重构树,这样我们就得到了一个只跟海拔相关的关系。
于是对于任意水位线,我们都可以得知,在不超过这个水位线的情况下我们可以到达哪些点,也就是哪些点可以使用车子到达,既然我们已经可以知道这个东西了,显然我们只需要在这些点中找到使得步行距离最短的点即可。
因为要使得步行路径最短,所以要先预处理出每个点到1的最短路,然后每个点的点权就是它到1号节点的最短路了,然后每次查询的时候,不断倍增向上跳,直到水位线超过海拔就停下,因为重构树的特性,所有可以到达的节点都会在你当前停下的这个点下面(属于这个点的子树),
因此我们找到了这个停下的节点,就已经知道我们可以到达哪些点了,这个时候我们要做的就是查询这个节点管理的叶子节点内权值最小的那个,这个可以在预处理倍增数组的时候用树形DP求出来,每个节点的点权即当前节点子树内的权值最小值。
于是对于每次查询,我们只需要倍增找到对应的停下节点,然后查询这个节点的点权即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 300100 5 #define ac 800100 6 7 int n, m, cnt; 8 int s[AC * 2], g[AC * 2], belong[AC * 2], dis[AC]; 9 int f[AC * 2][23]; 10 int Head[AC * 2], Next[ac], date[ac], len[ac], tot; 11 int Head2[AC * 2], Next2[ac], date2[ac], tot2; 12 bool vis[AC]; 13 14 struct road{ 15 int x, y, high; 16 }way[ac]; 17 18 struct node{ 19 int x, dis; 20 }; 21 22 struct cmp{ 23 bool operator () (node a, node b) 24 { 25 return a.dis > b.dis; 26 } 27 }; 28 29 priority_queue<node, vector<node>, cmp> q; 30 31 inline int read() 32 { 33 int x = 0;char c = getchar(); 34 while(c > '9' || c < '0') c = getchar(); 35 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 36 return x; 37 } 38 39 inline void add(int f, int w, int S) 40 { 41 date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, len[tot] = S; 42 date[++tot] = f, Next[tot] = Head[w], Head[w] = tot, len[tot] = S; 43 } 44 45 inline int Min(int a, int b) 46 { 47 if(a < b) return a; 48 else return b; 49 } 50 51 inline void upmin(int &a, int b) 52 { 53 if(b < a) a = b; 54 } 55 56 inline bool cmp1(road a, road b) 57 { 58 return a.high > b.high; 59 } 60 61 #define date date2 62 #define Next Next2 63 #define tot tot2 64 #define Head Head2 65 66 inline void add2(int f, int w) 67 { 68 date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, belong[w] = f; 69 } 70 71 void dfs(int x) 72 { 73 int now; 74 g[x] = INT_MAX; 75 for(R i = 1; i <= 22; i ++) 76 f[x][i] = f[f[x][i - 1]][i - 1]; 77 if(x <= n) g[x] = dis[x]; 78 for(R i = Head[x]; i; i = Next[i]) 79 { 80 now = date[i]; 81 //if(f[x][0] == now) continue; 82 f[now][0] = x; 83 dfs(now); 84 upmin(g[x], g[now]); 85 } 86 } 87 88 #undef date 89 #undef Next 90 #undef tot 91 #undef Head 92 93 void init() 94 { 95 memset(Head, 0, sizeof(Head)); 96 memset(Head2, 0, sizeof(Head2)); 97 memset(dis, 127, sizeof(dis)); 98 //memset(g, 127, sizeof(g)); 99 memset(vis, 0, sizeof(vis)); 100 tot = tot2 = 0; 101 //memset(f, 0, sizeof(f)); 102 } 103 104 void pre() 105 { 106 n = cnt = read(), m = read(); 107 for(R i = 1; i <= m; i ++) 108 { 109 int a = read(), b = read(), c = read(), d = read(); 110 way[i] = (road){a, b, d}; 111 add(a, b, c); 112 } 113 sort(way + 1, way + m + 1, cmp1); 114 } 115 116 void dij() 117 { 118 node x; int now; 119 dis[1] = 0, q.push((node){1, 0}); 120 while(!q.empty()) 121 { 122 x = q.top(), q.pop(); 123 while(vis[x.x] && !q.empty()) x = q.top(), q.pop(); 124 vis[x.x] = true; 125 for(R i = Head[x.x]; i; i = Next[i]) 126 { 127 now = date[i]; 128 if(dis[now] > dis[x.x] + len[i]) 129 { 130 dis[now] = dis[x.x] + len[i]; 131 q.push((node){now, dis[now]}); 132 } 133 } 134 } 135 } 136 137 int find(int x) 138 { 139 if(belong[x] == x) return x; 140 else return belong[x] = find(belong[x]); 141 } 142 143 void build() 144 { 145 int b = n + n; 146 for(R i = 1; i <= b; i ++) belong[i] = i; 147 for(R i = 1; i <= m; i ++) 148 { 149 int fx = find(way[i].x), fy = find(way[i].y); 150 if(fx == fy) continue; 151 s[++cnt] = way[i].high; 152 add2(cnt, fx), add2(cnt, fy); 153 } 154 for(R i = 0; i <= 22; i ++) f[cnt][i] = 0; 155 } 156 157 int jump(int x, int d) 158 { 159 for(R i = 22; i >= 0; i--) 160 if(s[f[x][i]] > d) x = f[x][i]; 161 return g[x]; 162 } 163 164 void work() 165 { 166 int now = 0, all = read(), k = read(), tt = read(); 167 for(R i = 1; i <= all; i ++) 168 { 169 int a = read(), b = read(); 170 a = (a + k * now - 1) % n + 1; 171 b = (b + k * now) % (tt + 1); 172 printf("%d\n", now = jump(a, b)); 173 } 174 } 175 176 int main() 177 { 178 freopen("10.in", "r", stdin); 179 int T = read(); 180 while(T --) 181 { 182 init();//初始化 183 pre();//读入 184 build();//重构树 185 dij();//最短路 186 dfs(cnt);//树上倍增 187 work();//回答询问 188 } 189 fclose(stdin); 190 return 0; 191 }