Starry Landscape Photo题解
题意简述
因为前
序列
B_1,B_2,\dots,B_n 是1,2,3,\dots,n 的一个排列,对于正整数1 \le l < r \le n 与1 \le b \le n ,有非空集合T = \{ B_i \mid l \le i \le r \text{ and } B_i \le b \} 求共有多少种不同的
T 。
暴力
考虑枚举所有的
正解
推理
因为枚举决定集合的参数
由于
- 每一个
T ,一定有一个最大值\max(T) \le b 。既然其为最大值,那么将b 改为\max(T) 后生成的集合也一定与T 完全一样,因为原集合中没有(\max(T), b] 间的数字。
因此不需要关注
而当
-
m \in T -
T$ 中每个元素都 $\le m -
那么就相当于把原序列中所有大于
于是,问题就分解成了:对于每一个
编码
要遍历每一个
// global
int p[500005];
// main
for (int i = 1; i <= n; i++) {
cin >> a[i];
p[a[i]] = i;
}
然后进行循环,对于每一个
但是,如果从小到大循环,则上一次的所有元素一定也在这次的保留序列中,也就是集合
- 左端点
l 一定在S_m 的第一个元素和m 的位置之间 - 右端点
r 一定在m 和S_m 的最后一个元素之间
由于枚举顺序,明显
单点修改,区间查询,最合适的数据结构是——树状数组!
// global
int tree[500005];
int lowbit(int x) {
return x & (-x);
}
void add(int x, int v) {
for (; x <= n; x += lowbit(x)) {
tree[x] += v;
}
}
int query(int x) {
int res = 0;
for (; x > 0; x -= lowbit(x)) {
res += tree[x];
}
return res;
}
最后,对于
// main
ll res = 0;
for (int i = 1; i <= n; ++i) {
int pos = p[i];
int k = query(pos) + 1;
res += (ll)k * (i - k + 1);
add(pos, 1);
}
cout << res << endl;
完整代码 | Accepted