ABC407F - Sums of Sliding Window Maximum

問題
整数列が与えられます。について、以下の問題を解いて下さい。
- の長さの連続部分列は個存在するが、それぞれについての最大値を合計したものを求めよ。
解法
問題をそのまま解くのは不可能なので、代わりに各値について寄与を考える。すなわち、が長さの連続部分列の最大値として現れるのは何回か?を考えれば良い。
が長さの連続部分列の最大値として現れることを考えるために、以下の要素が、の左右にいくつ伸びているか?を知りたい。これはstd::setとlower_boundを用いることで問題なく取得することができる。以降、をかつを満たす最小値、をかつを満たす最大値として定義する。
この区間において、を含んでかつ長さの連続部分列がいくつ存在するかを考えよう1。 まず、である間は、ちょうど個の連続部分列が存在する。これは左右にいくらでも伸ばせるので、を長さのうち何番目に置くか?という話を考えれば良い。次にである間、片端がより伸ばせず、もう片方はいくらでも伸ばせるのでのままである。最後にの場合は、両端に自由度がなくなっていくので、となる。
答えに対する寄与の回数を、例えばで考えると、となる。これは左右が等差数列、真ん中が同じ値になっていて簡単に区間演算をするのが難しい2が、「増加部分の+と減少部分の-を区間加算」ができればimos法で求めることができるので、区間加算の遅延セグメントツリーを用いれば良い3。
以上を実装することで、全体で時間で解くことができる。提出(C++, 249ms)
// 区間加算にしか興味がないので、Sの演算は何でもOK
using S = long long;
using F = long long;
const S INF = 8e18;
S op(S a, S b){ return std::min(a, b); }
S e(){ return INF; }
S mapping(F f, S x){ return f+x; }
F composition(F f, F g){ return f+g; }
F id(){ return 0; }
void solve() {
int n; cin>> n;
auto a = i64_vec_IN(n);
vll indicate(n,0); iota(all(indicate), 0);
sort(all(indicate), [&](const ll& i, const ll& j) {
if(a[i] == a[j]) return i>j;
else return a[i]<a[j];
});
lazy_segtree<S,op,e,F,mapping,composition,id> seg(n+1);
rep(i,n) seg.set(i,0);
set<ll> notChecked;
rep(i,n) notChecked.insert(i);
notChecked.insert(-1);
notChecked.insert(n);
for(auto i: indicate) {
notChecked.erase(i);
auto itr = notChecked.upper_bound(i);
ll right = *itr;
itr--;
ll left = *itr;
ll r_d = right - i;
ll l_d = i - left;
ll itrval = r_d + l_d - 1;
seg.apply(0, min(l_d, r_d), a[i]);
seg.apply(max(l_d, r_d), itrval+1, -a[i]);
}
ll cur = 0;
vll ans(n);
rep(i,n) {
cur += seg.get(i);
ans[i] = cur;
}
rep(i,n) cout << ans[i] << "
";
}