ST表 = 单调栈 + 并查集优化 || P1198 [JSOI2008] 最大数
其实这个题不能算是ST表吧,可以算是一个初等数据结构题,有很多做法。

做法一:用反向ST表做,因为每次操作都是暴力在数组末尾加数,所以ST表可以实现O(logn)的插入(具体反向思想我还没搞懂,搞懂了回来补)。
代码 :
1 #include <bits/stdc++.h>
2 #define gogo ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
3 using namespace std;
4 //using i64 = long long;
5 //#define endl '\n';
6 const string YES = "Yes";
7 const string NO = "No";
8
9 const int N = 200010;
10 int f[N][21], lg[N], a[N];
11 int T;
12 void change(int u) {
13 f[u][0] = a[u];
14 for (int i = 1;u - (1 << i) >= 0;i ++)
15 f[u][i] = max(f[u][i - 1], f[u - (1 << (i - 1))][i - 1]);
16 }
17 long long find(int l, int r) {
18 int k = lg[r - l + 1];
19 //cout << "ZZZ" << ' ' << l << ' ' << r << ' ' << k << endl;
20 //cout << "SSS" << ' ' << f[l][k] << ' ' << f[r - (1 << k) + 1][k] << endl;
21 //return max(f[l][k], f[r - (1 << k) + 1][k]);
22 return max(f[r][k],f[l + (1 << k) - 1][k]);
23 }
24
25 int main() {
26 int n, mod;
27 cin >> n >> mod;
28 lg[1] = 0;
29 for (int i = 2;i <= n;i ++)
30 lg[i] = lg[i >> 1] + 1;
31 int idx = 0;
32 for (int i = 1;i <= n;i ++) {
33 char o;
34 cin >> o;
35 if (o == 'A') {
36 int x;
37 cin >> x;
38 x = (x + T) % mod;
39 a[++idx] = x;
40 change(idx);
41 } else {
42 int k;
43 cin >> k;
44 if (k == 1) {
45 T = a[idx];
46 cout << a[idx] << '\n';
47 }
48 else {
49 T = find(idx - k + 1, idx);
50 cout << T << '\n';
51 }
52 }
53 }
54 }
做法二:可以用一个单调栈维护一下坐标。一开始想到了做法但是没找到单调性,其实是有单调性的因为如果新插入的数字要比栈顶所有元素大那么栈顶这个元素就没有贡献了(有单调性的重要原因是这个题查询是只查询后几个元素而不是随机区间,也就是说后面如果有比当前位置大的数那么当前位置的数一定没有贡献),所以用一个单调递减的单调栈维护。并且此时查询的时候也可以二分一下(查询栈中第一个大于等于查询数字的坐标),总体时间复杂度也是O(nlogn)的。
代码 :
1 #include <algorithm>
2 #include <bits/stdc++.h>
3 #define gogo ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
4 using namespace std;
5 //using i64 = long long;
6 //#define endl '\n';
7 const string YES = "Yes";
8 const string NO = "No";
9
10 const int N = 200010;
11 int stk[N], tt, a[N], T;
12 int main() {
13 int n, p;
14 cin >> n >> p;
15 int idx = 0;
16 tt = T = 0;
17 for (int i = 0;i < n;i ++) {
18 char o;
19 cin >> o;
20 if (o == 'A') {
21 int x;
22 cin >> x;
23 a[++ idx] = (x + T) % p;
24 while (tt && a[idx] > a[stk[tt]]) tt --;
25 stk[++tt] = idx;
26 } else {
27 int l;
28 cin >> l;
29 int ans = a[*lower_bound(stk + 1, stk + 1 + tt, idx - l + 1)];
30 T = ans;
31 cout << ans << '\n';
32 }
33 }
34 }
做法二小优化 :可以在查询的时候用并查集优化二分。在元素出栈的时候可以发现当前元素的贡献应该归属于将它弹出元素的贡献,也就是说查询当前位置元素时候相当于查询它归属元素的贡献,那么就可以在这个元素出栈的时候将它与将它弹出的元素合并,并且以将它弹出的元素作为父亲,这样查询的时候就是查询当前元素所属于集合的代表元。那么并查集按秩合并后查询约等于线性的,但是在模拟维护单调栈的时候会有一个常数?(我自己认为的因为维护单调栈每个元素会进出一次,外面还挂着一个O(n),应该也是O(n)但是会有一点点不同?),所以可能应该是平均情况O(n),最坏应该能卡到O(logn),不过这个题数据还是比较小的,看一下优化后的情况 :

可以看到优化了100ms左右,看来是几乎线性了我觉得。
做法都来源于luogu评论区,这个单调栈的做法基本能搞出来但是一开始没找到线性的就回去搞ST表了。。。。
代码 :
1 #include <algorithm> 2 #include <bits/stdc++.h> 3 #define gogo ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL); 4 using namespace std; 5 //using i64 = long long; 6 //#define endl '\n'; 7 const string YES = "Yes"; 8 const string NO = "No"; 9 10 const int N = 200010; 11 int stk[N], tt, a[N], T; 12 int p[N]; 13 int find(int x) { 14 if (p[x] != x) p[x] = find(p[x]); 15 return p[x]; 16 } 17 int main() { 18 int n, m; 19 cin >> n >> m; 20 int idx = 0; 21 tt = T = 0; 22 for (int i = 0;i < n;i ++) { 23 char o; 24 cin >> o; 25 if (o == 'A') { 26 int x; 27 cin >> x; 28 a[++ idx] = (x + T) % m; 29 p[idx] = idx; 30 while (tt && a[idx] > a[stk[tt]]) p[find(stk[tt --])] = find(idx); 31 stk[++tt] = idx; 32 } else { 33 int l; 34 cin >> l; 35 //int ans = a[*lower_bound(stk + 1, stk + 1 + tt, idx - l + 1)]; 36 int ans = a[find(idx - l + 1)]; 37 T = ans; 38 cout << ans << '\n'; 39 } 40 } 41 }


浙公网安备 33010602011771号