[题解]CF1988E Range Minimum Sum
思路
如果没有删除操作,就是一个典中典。
直接枚举最小值 \(a_i\),可以轻松 \(\Theta(n)\) 找到其所能管辖到的最大区间 \([L_i,R_i]\)。形式化地说,找到一个最小的 \(L_i\) 和一个最大的 \(R_i\),使得 \(\min_{L_i \leq x \leq R_i}\{a_x\} = a_i\)。
那么它对答案的贡献是 \(a_i \times (i - L_i + 1) \times (R_i - i + 1)\)。
加入删除操作,也是差不多的做法,依旧是枚举最小值。可以按照删除元素的位置 \(t\) 进行如下分讨:
-
\(t \in [1,L_i - 1)\) 或 \(t \in (R_i + 1,n]\),显然对答案无影响,贡献为 \(a_i \times (i - L_i + 1) \times (R_i - i + 1)\)。
-
\(t \in [L_i,i)\),在 \(i\) 左边的选择会少一种,贡献为 \(a_i \times (i - L_i) \times (R_i - i + 1)\)。
-
\(t \in (i,R_i]\),在 \(i\) 右边的选择会少一种,贡献为 \(a_i \times (i - L_i + 1) \times (R_i - i)\)。
-
\(t = L_i - 1\) 或 \(t = R_i + 1\),比较特殊的一种情况,当删除这个位置后,\(a_i\) 对应新的区间范围可能变化,需要重新求解。可以直接利用 ST 表二分求解。
所有贡献都是连续的,直接差分计算即可。
Code
#include <bits/stdc++.h>
#define re register
#define fst first
#define snd second
#define ll long long
using namespace std;
typedef pair<int,int> pii;
const int N = 5e5 + 10,M = 24,inf = 1e9 + 10;
int n;
int arr[N],L[N],R[N],lg[N];
ll c[N];
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
struct ST{
#define pot(x) (1 << x)
pii dp[N][M];
inline pii Add(pii a,pii b){
if (a.fst == b.fst) return {a.fst,min(a.snd,b.snd)};
else if (a.fst < b.fst) return {a.fst,min(a.snd,b.fst)};
else return {b.fst,min(a.fst,b.snd)};
}
inline void build(){
for (re int i = 2;i <= n;i++) lg[i] = lg[i >> 1] + 1;
for (re int i = 1;i <= n;i++) dp[i][0] = {arr[i],inf};
for (re int j = 1;j <= lg[n];j++){
for (re int i = 1;i + pot(j) - 1 <= n;i++) dp[i][j] = Add(dp[i][j - 1],dp[i + pot(j - 1)][j - 1]);
}
}
inline pii query(int l,int r){
int len = r - l + 1;
return Add(dp[l][lg[len]],dp[r - pot(lg[len]) + 1][lg[len]]);
}
#undef pot
}st;
inline void add(int l,int r,ll k){
if (l > r) return;
c[l] += k; c[r + 1] -= k;
}
inline void solve(){
n = read();
fill(c + 1,c + n + 5,0);
for (re int i = 1;i <= n;i++) arr[i] = read();
st.build();
for (re int i = 1;i <= n;i++) L[i] = R[i] = i;
for (re int i = 1;i <= n;i++){
while (L[i] > 1 && arr[L[i] - 1] > arr[i]) L[i] = L[L[i] - 1];
}
for (re int i = n;i;i--){
while (R[i] < n && arr[R[i] + 1] > arr[i]) R[i] = R[R[i] + 1];
}
for (re int i = 1;i <= n;i++){
int lnum = i - L[i] + 1,rnum = R[i] - i + 1;
add(1,L[i] - 2,1ll * lnum * rnum * arr[i]);
add(L[i],i - 1,1ll * (lnum - 1) * rnum * arr[i]);
add(i + 1,R[i],1ll * lnum * (rnum - 1) * arr[i]);
add(R[i] + 2,n,1ll * lnum * rnum * arr[i]);
if (L[i] > 1){
int l = 1,r = L[i] - 1;
while (l < r){
int mid = l + r >> 1;
if (st.query(mid,i).snd == arr[i]) r = mid;
else l = mid + 1;
}
int Lnum = i - l;
add(L[i] - 1,L[i] - 1,1ll * Lnum * rnum * arr[i]);
}
if (R[i] < n){
int l = R[i] + 1,r = n;
while (l < r){
int mid = l + r + 1 >> 1;
if (st.query(i,mid).snd == arr[i]) l = mid;
else r = mid - 1;
}
int Rnum = l - i;
add(R[i] + 1,R[i] + 1,1ll * lnum * Rnum * arr[i]);
}
}
for (re int i = 1;i <= n;i++) printf("%lld ",c[i] += c[i - 1]);
puts("");
}
signed main(){
int T; T = read();
while (T--) solve();
return 0;
}

浙公网安备 33010602011771号