Dice Wars
https://tioj.ck.tp.edu.tw/problems/1726
Description
Dice Wars是一款兼具謀略和運氣的遊戲。
遊戲中你扮演紫色的骰子,要攻下其他顏色的骰子的城池,進而統一全地圖。
如今你選到了一張看起來不錯的地圖: 整張地圖呈一條直線,每個位置都有一個顏色勢力佔領。
由於每次移動到相鄰異色的城池都必須經歷一場鏖戰,你想先經過程式計算後再進行遊戲。
你想要每次詢問一個顏色對$(S, T)$,問從任何一個$S$的城池到任一個$T$的城池至少要經過幾場戰鬥。
如果$S$或$T$已經滅亡(地圖中沒有任何一個該勢力),就輸出$-1$。
Solution
題敘裡面附上的遊戲好好玩www
題目要問的其實就是$\min\limits _ {c_i=S,c_j=T}(|i-j|)$
可以想到對每種顏色開一個 vector
紀錄他們的index
一種naive$\mathcal{O}(nq)$的方法是每次詢問都直接把兩種顏色的兩個 vector
merge
$\mathcal{O}(n)$合併並計算答案
而另一種naive的算法則是先針對每一種顏色$\mathcal{O}(n)$預處理其對其他顏色的答案,複雜度$\mathcal{O}(n^2+q)$
前者拉低複雜度的關鍵是某種顏色出現很多次
而後者則是會因為太多種顏色而複雜度爛掉
怎麼辦呢?可以不要全部預處理,只針對出現次數超過$k$的顏色做預處理,這些顏色的種類數不會超過$\frac{n}{k}$種
故預處理需要$\mathcal{O}(\frac{n^2}{k})$
而對於詢問的兩個顏色的出現次數都沒有超過$k$的情況,可以直接用上面第一個算法處理
複雜度$\mathcal{O}(qk)$
根據算幾不等式可取$k=\frac{n}{\sqrt{q}}$有複雜度$\mathcal{O}(n\sqrt{q})$
註: 這題我寫的時候 ans
開原生陣列MLE,不知為何用vector陣列會是好的
AC code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
| #include <bits/stdc++.h>
#ifdef local
#define debug(x) (cerr<<#x<<" = "<<(x)<<'\n')
#else
#define debug(x) ((void)0)
#endif // local
#define pb emplace_back
using namespace std;
constexpr int N = 60025, K = 300, inf = 1e8;
vector<int> id[N],ans[K];
int big[N],totb,n,q,v[N];
void precalc(int k) {
ans[k].resize(n+1,inf);
//for(int i = 1; i <= n; i++) ans[k][i] = inf;
int last;
last = -inf;
for(int i = 0; i < n; i++) {
if(big[v[i]] == k)
last = i;
else
ans[k][v[i]] = min(ans[k][v[i]], i - last);
}
last = inf;
for(int i = n-1; i >= 0; i--) {
if(big[v[i]] == k)
last = i;
else
ans[k][v[i]] = min(ans[k][v[i]], last - i);
}
}
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0);
// small - small -> brute every time
// big - other -> precalc
cin >> n >> q;
int S = 400;
for(int i = 0; i < n; i++) cin >> v[i];
for(int i = 0; i < n; i++) id[v[i]].pb(i);
for(int i = 1; i <= n; i++) if(id[i].size() >= S) {
big[i] = ++totb;
precalc(big[i]);
}
while(q--) {
int a,b;
cin >> a >> b;
if(id[a].empty() || id[b].empty()) cout << -1 << '\n';
else if(a == b) cout << 0 << '\n';
else {
if(id[a].size() < S && id[b].size() < S) {
vector<int> A = id[a];
vector<int> B = id[b];
int i = 0, j = 0, lastA = -inf, lastB = -inf, res = inf;
while(i < A.size() || j < B.size()) {
if(j==B.size() || (i<A.size() && A[i]<B[j])) {
int t = A[i++];
res = min(res, t - lastB);
lastA = t;
}else {
int t = B[j++];
res = min(res, t - lastA);
lastB = t;
}
}
cout << res << '\n';
}else {
if(big[a])
cout << ans[big[a]][b] << '\n';
else
cout << ans[big[b]][a] << '\n';
}
}
}
}
|