Luogu3350 ZJOI2016 旅行者 最短路、分治
题意:给出一个$N \times M$的网格图,边有边权,$Q$组询问,每组询问$(x_1,y_1)$到$(x_2,y_2)$的最短路。$N \times M \leq 2 \times 10^4 , Q \leq 10^5$
BZOJ原题竟然没有数据范围
矩形的多组询问问题考虑分治。考虑计算矩形$(x_1,y_1,x_2,y_2)$的询问,我们将较长边沿着中线劈成两半,在这些询问里面就只可能存在两种情况:①询问的两个端点在中线两边,那么路径就一定会经过中线上一点;②询问的两个端点在中线的同一边,那么有可能不会经过中线,也有可能经过中线。所以我们对中线上所有的点跑一边整个矩形的最短路,每一次跑完最短路就更新所有的询问,然后再递归进入两侧矩形回答②类型的询问。时间复杂度似乎是$O(NM \times \sqrt{NM} \times log(NM))$
在$Luogu$题解上学到的一个加速方法:从一个点的最短路转移到新的点跑最短路的时候,可以不将最短路数组赋值为极大值,而是赋值为这一个点经过上一个计算的点到达所有目标点的答案。
(然而还是在Luogu不开O2的情况下TLE)
1 // luogu-judger-enable-o2
2 //This code is written by Itst
3 #include<bits/stdc++.h>
4 #define pos(i,j) ((i-1)*M+j)
5 using namespace std;
6
7 inline int read(){
8 int a = 0;
9 bool f = 0;
10 char c = getchar();
11 while(c != EOF && !isdigit(c)){
12 if(c == '-')
13 f = 1;
14 c = getchar();
15 }
16 while(c != EOF && isdigit(c)){
17 a = (a << 3) + (a << 1) + (c ^ '0');
18 c = getchar();
19 }
20 return f ? -a : a;
21 }
22
23 const int MAXN = 100010;
24 int cntEd , N , M , Q , minDis[MAXN] , ans[MAXN] , head[MAXN];
25 struct Edge{
26 int end , upEd , w;
27 }Ed[MAXN << 2];
28 struct query{
29 int sx , sy , ex , ey , ind;
30 }now[MAXN] , pot[MAXN];
31 priority_queue < pair < int , int > > q;
32
33 inline void addEd(int a , int b , int c){
34 Ed[++cntEd].end = b;
35 Ed[cntEd].w = c;
36 Ed[cntEd].upEd = head[a];
37 head[a] = cntEd;
38 }
39
40 void Dijk(int bx , int by , int lx , int ly , int rx , int ry , int l){
41 int temp = minDis[pos(bx , by)];
42 for(int i = lx ; i <= rx ; i++)
43 for(int j = ly ; j <= ry ; j++)
44 minDis[pos(i , j)] = l == 0 ? 0x3f3f3f3f : minDis[pos(i , j)] + temp;
45 minDis[pos(bx , by)] = 0;
46 q.push(make_pair(0 , pos(bx , by)));
47 while(!q.empty()){
48 pair < int , int > t = q.top();
49 q.pop();
50 if(minDis[t.second] != -t.first)
51 continue;
52 for(int i = head[t.second] ; i ; i = Ed[i].upEd)
53 if(minDis[Ed[i].end] > minDis[t.second] + Ed[i].w){
54 minDis[Ed[i].end] = minDis[t.second] + Ed[i].w;
55 q.push(make_pair(-minDis[Ed[i].end] , Ed[i].end));
56 }
57 }
58 }
59
60 void solve(int lx , int ly , int rx , int ry , int ql , int qr){
61 if(ql > qr)
62 return;
63 if(rx - lx > ry - ly){
64 int mid = lx + rx >> 1;
65 for(int i = ly ; i <= ry ; i++){
66 Dijk(mid , i , lx , ly , rx , ry , i - ly);
67 for(int j = ql ; j <= qr ; j++)
68 ans[now[j].ind] = min(ans[now[j].ind] , minDis[pos(now[j].sx , now[j].sy)] + minDis[pos(now[j].ex , now[j].ey)]);
69 }
70 int p1 = ql , p2 = qr;
71 for(int i = ql ; i <= qr ; i++)
72 if(now[i].sx < mid && now[i].ex < mid)
73 pot[p1++] = now[i];
74 else
75 if(now[i].sx > mid && now[i].ex > mid)
76 pot[p2--] = now[i];
77 memcpy(now + ql , pot + ql , sizeof(query) * (qr - ql + 1));
78 solve(lx , ly , mid , ry , ql , p1 - 1);
79 solve(mid + 1 , ly , rx , ry , p2 + 1 , qr);
80 }
81 else{
82 int mid = ly + ry >> 1;
83 for(int i = lx ; i <= rx ; i++){
84 Dijk(i , mid , lx , ly , rx , ry , i - lx);
85 for(int j = ql ; j <= qr ; j++)
86 ans[now[j].ind] = min(ans[now[j].ind] , minDis[pos(now[j].sx , now[j].sy)] + minDis[pos(now[j].ex , now[j].ey)]);
87 }
88 int p1 = ql , p2 = qr;
89 for(int i = ql ; i <= qr ; i++)
90 if(now[i].sy < mid && now[i].ey < mid)
91 pot[p1++] = now[i];
92 else
93 if(now[i].sy > mid && now[i].ey > mid)
94 pot[p2--] = now[i];
95 memcpy(now + ql , pot + ql , sizeof(query) * (qr - ql + 1));
96 solve(lx , ly , rx , mid , ql , p1 - 1);
97 solve(lx , mid + 1 , rx , ry , p2 + 1 , qr);
98 }
99 }
100
101 int main(){
102 #ifdef LG
103 freopen("3350.in" , "r" , stdin);
104 freopen("3350.out" , "w" , stdout);
105 #endif
106 memset(ans , 0x3f , sizeof(ans));
107 N = read();
108 M = read();
109 for(int i = 1 ; i <= N ; i++)
110 for(int j = 1 ; j < M ; j++){
111 int t = read();
112 addEd(pos(i , j) , pos(i , j + 1) , t);
113 addEd(pos(i , j + 1) , pos(i , j) , t);
114 }
115 for(int i = 1 ; i < N ; i++)
116 for(int j = 1 ; j <= M ; j++){
117 int t = read();
118 addEd(pos(i , j) , pos(i + 1 , j) , t);
119 addEd(pos(i + 1 , j) , pos(i , j) , t);
120 }
121 Q = read();
122 for(int i = 1 ; i <= Q ; i++){
123 now[i].sx = read();
124 now[i].sy = read();
125 now[i].ex = read();
126 now[i].ey = read();
127 now[i].ind = i;
128 }
129 solve(1 , 1 , N , M , 1 , Q);
130 for(int i = 1 ; i <= Q ; i++)
131 printf("%d\n" , ans[i]);
132 return 0;
133 }