[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
", 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 }
View Code
原文地址:https://www.cnblogs.com/ww3113306/p/9710505.html