第四屆CCF軟件能力認證(CSP2015) 第五題(最小花費)題解


【問題描述】

  C國共有$n$個城市。有$n-1$條雙向道路,每條道路連接兩個城市,任意兩個城市之間能互相到達。小R來到C國旅行,他共規划了$m$條旅行的路線, 第$i$條旅行路線的起點是$s_i$,終點是$t_i$。在旅行過程中,小R每行走一單位長度的路需要吃一單位的食物。C國的食物只能在各個城市中買到,而且不同城市的食物價格可能不同。
  然而,小R不希望在旅行中為了購買較低價的糧食而繞遠路,因此他總會選擇最近的路走。現在,請你計算小R規划的每條旅行路線的最小花費是多少。

【輸入格式】

  第一行包含2個整數$n$和$m$。
  第二行包含$n$個整數。第$i$個整數$w_i$表示城市$i$的食物價格。
  接下來$n-1$行,每行包括3個整數$u, v, e$,表示城市$u$和城市$v$之間有一條長為$e$的雙向道路。
  接下來$m$行,每行包含2個整數$s_i$和$t_i$,分別表示一條旅行路線的起點和終點。

【輸出格式】

  輸出$m$行,分別代表每一條旅行方案的最小花費。

【樣例輸入】

6 4
1 7 3 2 5 6
1 2 4
1 3 5
2 4 1
3 5 2
3 6 1
2 5
4 6
6 4
5 6

【樣例輸出】

35
16
26
13

【樣例說明】

對於第一條路線,小R會經過2->1->3->5。其中在城市2處以7的價格購買4單位糧食,到城市1時全部吃完,並用1 的價格購買7單位糧食,然后到達終點。

【評測用例規模與約定】

  前10%的評測用例滿足:$n, m ≤ 20, w_i ≤ 20$;
  前30%的評測用例滿足:$n, m ≤ 200$;
  另有40%的評測用例滿足:一個城市至多與其它兩個城市相連。
  所有評測用例都滿足:$1 ≤ n, m ≤ 10^5,1 ≤ w_i ≤ 10^6,1 ≤ e ≤ 10000$。

 

【題解】

首先注意到,一條路徑的選擇方案,一定是從一個點走到下一個比它便宜的點,這之間的食物都在這個點購買。

而這個信息不具有可加性,卻具有可減性。

之前在網上搜到了一篇自稱要維護兩遍單調棧三個lct的博客,但是維護單調棧的最壞時間復雜度為$O(n^2)$。

 

下面介紹一種基於點分治的做法。

假設當前分治中心為T,對於一個詢問u->v,可以被拆成u->T,T->v,對於u->T的費用,我們可以在倍增數組上二分出u上面第一個比它便宜的位置,用這個位置的信息可以直接得出u的信息。

現在考慮T->v的費用,對於每一個詢問,都附加了一個狀態,表示之前便宜的費用c,我們需要在T->v的路徑上找到第一個比它便宜的點設為x(這個可以通過dfs時維護一個前綴最小值數據來二分求得),這一段用的費用是dis(T, x) * c,剩下的部分就是從x向下走走到v的費用,我們可以通過dfs求出每個點到T的費用,之前已經提到過,維護的信息具有可減性,就可以$O(1)$的時間算出從一個點往下走的走到某個點的費用。

至此,問題在$O(n\log^2n)$的時間復雜度,$O(n\log n)$的空間復雜度內解決。

 

【代碼】(濫用stl導致常數非常大)

  1 #include<bits/stdc++.h>
  2 
  3 using namespace std;
  4 
  5 typedef long long LL;
  6 typedef pair<int, int> pii;
  7 #define FI first
  8 #define SE second
  9 #define for_edge(u, it) for(vector<pii>::iterator it = G[u].begin(); it != G[u].end(); ++it)
 10 
 11 const int N = 100000 + 10;
 12 
 13 vector<pii> G[N];
 14 vector<int> Q[N], q1[N], q2[N];
 15 LL dis[N], disv[N];
 16 int cost[N], sz[N], maxsz[N], top[N], root;
 17 pii q_info[N];
 18 pair<LL, int> ans[N];
 19 bool centre[N];
 20 
 21 #define v it->FI
 22 void get_size(int u, int fa) {
 23     maxsz[u] = 0, sz[u] = 1;
 24     for_edge(u, it) if(v != fa && !centre[v]) {
 25         get_size(v, u);
 26         sz[u] += sz[v];
 27         maxsz[u] = max(maxsz[u], sz[v]);
 28     }
 29 }
 30 
 31 void get_root(int u, int fa, int r) {
 32     maxsz[u] = max(maxsz[u], sz[r] - sz[u]);
 33     if(maxsz[u] < maxsz[root]) root = u;
 34     for_edge(u, it) if(v != fa && !centre[v]) {
 35         get_root(v, u, r);
 36     }
 37 }
 38 
 39 int anc[17][N], val[17][N];
 40 
 41 int tot_time;
 42 
 43 void get_top(int u, int fa, int pre) {
 44     anc[0][u] = fa, val[0][u] = cost[u];
 45     int tmp = clock();
 46     for(int i = 1; i < 17; i++) {
 47         val[i][u] = min(val[i-1][u], val[i-1][anc[i-1][u]]);
 48         anc[i][u] = anc[i-1][anc[i-1][u]];
 49     }
 50     tot_time += clock() - tmp;
 51         
 52     top[u] = pre;
 53     for_edge(u, it) if(v != fa && !centre[v]) {
 54         dis[v] = dis[u] + it->SE, get_top(v, u, pre);
 55     }
 56 }
 57 
 58 int pre[N];
 59 
 60 int find(int u) {
 61     int cost_u = cost[u];
 62     //int tmp = clock();
 63     for(int i = 16; i >= 0; i--) {
 64         if(val[i][u] >= cost_u) u = anc[i][u];
 65     }
 66     //tot_time += clock() - tmp;
 67     return u;
 68 }
 69 
 70 void calc_1(int u, int fa) {
 71     int anc = find(u);
 72 
 73     if(anc) pre[u] = pre[anc];
 74     else anc = root, pre[u] = u; // be root when not exist
 75     disv[u] = (dis[u] - dis[anc]) * cost[u] + disv[anc];
 76     
 77     for(unsigned i = 0; i < q1[u].size(); i++) {
 78         ans[q1[u][i]] = make_pair(disv[u], cost[pre[u]]);
 79     }
 80     
 81     for_edge(u, it) if(v != fa && !centre[v]) {
 82         calc_1(v, u);
 83     }
 84 
 85 }
 86 
 87 void calc_2(int u, int fa, int fee) {
 88     static int val[N], id[N], tot;
 89     disv[u] = (dis[u] - dis[fa]) * fee + disv[fa];
 90     id[tot] = u, val[tot] = cost[u];
 91     if(tot++) val[tot-1] = min(val[tot-2], cost[u]);
 92 
 93     id[tot] = u; // be u when not exist
 94     for(unsigned i = 0; i < q2[u].size(); i++) {
 95         int c = q2[u][i];
 96         int anc = id[lower_bound(val, val + tot, ans[c].SE, greater<int>()) - val];
 97         ans[c].FI += ans[c].SE * (dis[anc] - dis[root]) + disv[u] - disv[anc];
 98     }
 99 
100     for_edge(u, it) if(v != fa && !centre[v]) {
101         calc_2(v, u, min(fee, cost[u]));
102     }
103     --tot;
104 }
105 
106 void solve(int u) {
107     if(!Q[u].size()) return;
108     
109     get_size(u, 0);
110     root = u, get_root(u, 0, u);
111     //cerr << sz[u] << ' ' << maxsz[root] << endl;
112     
113     vector<int> vec_q;
114     vec_q.swap(Q[u]);
115 
116     centre[u = root] = 1, top[u] = u, dis[u] = 0;
117     for(int i = 0; i < 17; i++) anc[i][u] = val[i][u] = 0;
118     val[0][u] = cost[u];
119     for_edge(u, it) if(!centre[v]) {
120         dis[v] = it->SE, get_top(v, u, v);
121     }
122 
123     for(unsigned i = 0; i < vec_q.size(); i++) {
124         int c = vec_q[i], x = q_info[c].FI, y = q_info[c].SE;
125         if(top[x] == top[y]) Q[top[x]].push_back(c);
126         else q1[x].push_back(c), q2[y].push_back(c);
127     }
128 
129 
130     disv[u] = 0, pre[u] = u;
131     calc_1(u, 0);
132 
133     disv[u] = 0;
134     calc_2(root, 0, cost[u]);
135 
136     for(unsigned i = 0; i < vec_q.size(); i++) {
137         int c = vec_q[i], x = q_info[c].FI, y = q_info[c].SE;
138         q1[x].clear(), q2[y].clear();
139     }
140 
141     for_edge(u, it) if(!centre[v]) {
142         solve(v);
143     }
144 }
145 #undef v
146 
147 int main() {
148 #ifdef DEBUG
149     freopen("in.txt", "r", stdin);
150     freopen("out.txt", "w", stdout);
151     int start_time = clock();
152 #endif
153 
154     int n, m; scanf("%d%d", &n, &m);
155     for(int i = 1; i <= n; i++) {
156         scanf("%d", cost + i);
157     }
158     for(int i = 1; i < n; i++) {
159         int u, v, w;
160         scanf("%d%d%d", &u, &v, &w);
161         G[u].push_back(pii(v, w));
162         G[v].push_back(pii(u, w));
163     }
164 
165     for(int i = 0; i < m; i++) {
166         pii &cur = q_info[i];
167         scanf("%d%d", &cur.FI, &cur.SE);
168         if(cur.FI != cur.SE) Q[1].push_back(i);
169     }
170 
171     solve(1);
172 
173     for(int i = 0; i < m; i++) {
174         printf("%I64d\n", ans[i].FI);
175     }
176 #ifdef DEBUG
177     fprintf(stderr, "time used : %.5fs, %.5fs\n", (double) (clock() - start_time) / CLOCKS_PER_SEC, (double)tot_time / CLOCKS_PER_SEC);
178 #endif
179     return 0;
180 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM